diff --git a/CMakeLists.txt b/CMakeLists.txt index e84d970750..7367f2e3a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,6 @@ 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") -set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL "Disable copying lib/ files to install prefix") set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation") if(ZIG_STATIC) @@ -414,19 +413,26 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") else() set(LIBUSERLAND_RELEASE_MODE "true") endif() -if(ZIG_SKIP_INSTALL_LIB_FILES) - set(ZIG_BUILD_INSTALL_STEP "") -else() - set(ZIG_BUILD_INSTALL_STEP "install") + +set(BUILD_LIBUSERLAND_ARGS "build" + --override-lib-dir "${CMAKE_SOURCE_DIR}/lib" + "-Doutput-dir=${CMAKE_BINARY_DIR}" + "-Drelease=${LIBUSERLAND_RELEASE_MODE}" + "-Dlib-files-only" + --prefix "${CMAKE_INSTALL_PREFIX}" + libuserland +) + +# When using Visual Studio build system generator we default to libuserland install. +if(MSVC) + set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL "Disable copying lib/ files to install prefix") + if(NOT ZIG_SKIP_INSTALL_LIB_FILES) + set(BUILD_LIBUSERLAND_ARGS ${BUILD_LIBUSERLAND_ARGS} install) + endif() endif() + add_custom_target(zig_build_libuserland ALL - COMMAND zig0 build - --override-lib-dir "${CMAKE_SOURCE_DIR}/lib" - libuserland ${ZIG_BUILD_INSTALL_STEP} - "-Doutput-dir=${CMAKE_BINARY_DIR}" - "-Drelease=${LIBUSERLAND_RELEASE_MODE}" - "-Dlib-files-only" - --prefix "${CMAKE_INSTALL_PREFIX}" + COMMAND zig0 ${BUILD_LIBUSERLAND_ARGS} DEPENDS zig0 BYPRODUCTS "${LIBUSERLAND}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" @@ -444,4 +450,15 @@ elseif(MINGW) target_link_libraries(zig ntdll) endif() add_dependencies(zig zig_build_libuserland) + install(TARGETS zig DESTINATION bin) + +# CODE has no effect with Visual Studio build system generator. +if(NOT MSVC) + get_target_property(zig0_BINARY_DIR zig0 BINARY_DIR) + install(CODE "set(zig0_EXE \"${zig0_BINARY_DIR}/zig0\")") + install(CODE "set(INSTALL_LIBUSERLAND_ARGS \"${BUILD_LIBUSERLAND_ARGS}\" install)") + install(CODE "set(BUILD_LIBUSERLAND_ARGS \"${BUILD_LIBUSERLAND_ARGS}\")") + install(CODE "set(CMAKE_SOURCE_DIR \"${CMAKE_SOURCE_DIR}\")") + install(SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/cmake/install.cmake) +endif() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5aeeb9e21..62b8083222 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,23 +51,28 @@ knowledge of Zig internals.** ### Editing Source Code -First, build the Stage 1 compiler as described in [the Building section](#building). +First, build the Stage 1 compiler as described in [Building from Source](README.md#Building-from-Source). -One modification you may want to make is adding `-DZIG_SKIP_INSTALL_LIB_FILES=ON` -to the cmake line. If you use the build directory as a working directory to run -tests with, zig will find the lib files in the source directory, and they will not -be "installed" every time you run `make`. This will allow you to make modifications -directly to the standard library, for example, and have them effective immediately. -Note that if you already ran `make` or `make install` with the default cmake -settings, there will already be a `lib/` directory in your build directory. When -executed from the build directory, zig will find this instead of the source lib/ -directory. Remove the unwanted directory so that the desired one can be found. +Zig locates lib files relative to executable path by searching up the +filesystem tree for a sub-path of `lib/zig/std/std.zig` or `lib/std/std.zig`. +Typically the former is an install and the latter a git working tree which +contains the build directory. + +During development it is not necessary to perform installs when modifying +stage1 or userland sources and in fact it is faster and simpler to run, +test and debug from a git working tree. + +- `make` is typically sufficient to build zig during development iterations. +- `make install` performs a build __and__ install. +- `msbuild -p:Configuration=Release INSTALL.vcxproj` on Windows performs a +build and install. To avoid install, pass cmake option `-DZIG_SKIP_INSTALL_LIB_FILES=ON`. To test changes, do the following from the build directory: -1. Run `make install` (on POSIX) or +1. Run `make` (on POSIX) or `msbuild -p:Configuration=Release INSTALL.vcxproj` (on Windows). -2. `bin/zig build test` (on POSIX) or `bin\zig.exe build test` (on Windows). +2. `$BUILD_DIR/zig build test` (on POSIX) or + `$BUILD_DIR/Release\zig.exe build test` (on Windows). That runs the whole test suite, which does a lot of extra testing that you likely won't always need, and can take upwards of 1 hour. This is what the @@ -85,8 +90,8 @@ Another example is choosing a different set of things to test. For example, not the other ones. Combining this suggestion with the previous one, you could do this: -`bin/zig build test-std -Dskip-release` (on POSIX) or -`bin\zig.exe build test-std -Dskip-release` (on Windows). +`$BUILD_DIR/bin/zig build test-std -Dskip-release` (on POSIX) or +`$BUILD_DIR/Release\zig.exe build test-std -Dskip-release` (on Windows). This will run only the standard library tests, in debug mode only, for all targets (it will cross-compile the tests for non-native targets but not run diff --git a/cmake/install.cmake b/cmake/install.cmake new file mode 100644 index 0000000000..415a088d6a --- /dev/null +++ b/cmake/install.cmake @@ -0,0 +1,37 @@ +message("-- Installing: ${CMAKE_INSTALL_PREFIX}/lib") + +if(NOT EXISTS ${zig0_EXE}) + message("::") + message(":: ERROR: Executable not found") + message(":: (execute_process)") + message("::") + message(":: executable: ${zig0_EXE}") + message("::") + message(FATAL_ERROR) +endif() + +execute_process(COMMAND ${zig0_EXE} ${INSTALL_LIBUSERLAND_ARGS} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + RESULT_VARIABLE _result +) +if(_result) + message("::") + message(":: ERROR: ${_result}") + message(":: (execute_process)") + + string(REPLACE ";" " " s_INSTALL_LIBUSERLAND_ARGS "${INSTALL_LIBUSERLAND_ARGS}") + message("::") + message(":: argv: ${zig0_EXE} ${s_INSTALL_LIBUSERLAND_ARGS} install") + + set(_args ${zig0_EXE} ${INSTALL_LIBUSERLAND_ARGS}) + list(LENGTH _args _len) + math(EXPR _len "${_len} - 1") + message("::") + foreach(_i RANGE 0 ${_len}) + list(GET _args ${_i} _arg) + message(":: argv[${_i}]: ${_arg}") + endforeach() + + message("::") + message(FATAL_ERROR) +endif() diff --git a/doc/langref.html.in b/doc/langref.html.in index 9509e34228..d0566bef7d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2893,6 +2893,47 @@ test "switch using enum literals" { } {#code_end#} {#header_close#} + + {#header_open|Non-exhaustive enum#} +

+ A Non-exhaustive enum can be created by adding a trailing '_' field. + It must specify a tag type and cannot consume every enumeration value. +

+

+ {#link|@intToEnum#} on a non-exhaustive enum cannot fail. +

+

+ A switch on a non-exhaustive enum can include a '_' prong as an alternative to an {#syntax#}else{#endsyntax#} prong + with the difference being that it makes it a compile error if all the known tag names are not handled by the switch. +

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +const Number = enum(u8) { + One, + Two, + Three, + _, +}; + +test "switch on non-exhaustive enum" { + const number = Number.One; + const result = switch (number) { + .One => true, + .Two, + .Three => false, + _ => false, + }; + assert(result); + const is_one = switch (number) { + .One => true, + else => false, + }; + assert(is_one); +} + {#code_end#} + {#header_close#} {#header_close#} {#header_open|union#} @@ -6815,6 +6856,19 @@ async fn func(y: *i32) void {

{#header_close#} + {#header_open|@bitSizeOf#} +
{#syntax#}@bitSizeOf(comptime T: type) comptime_int{#endsyntax#}
+

+ This function returns the number of bits it takes to store {#syntax#}T{#endsyntax#} in memory. + The result is a target-specific compile time constant. +

+

+ This function measures the size at runtime. For types that are disallowed at runtime, such as + {#syntax#}comptime_int{#endsyntax#} and {#syntax#}type{#endsyntax#}, the result is {#syntax#}0{#endsyntax#}. +

+ {#see_also|@sizeOf|@typeInfo#} + {#header_close#} + {#header_open|@breakpoint#}
{#syntax#}@breakpoint(){#endsyntax#}

@@ -8044,7 +8098,7 @@ test "@setRuntimeSafety" { This function measures the size at runtime. For types that are disallowed at runtime, such as {#syntax#}comptime_int{#endsyntax#} and {#syntax#}type{#endsyntax#}, the result is {#syntax#}0{#endsyntax#}.

- {#see_also|@typeInfo#} + {#see_also|@bitSizeOf|@typeInfo#} {#header_close#} {#header_open|@sliceToBytes#} diff --git a/lib/std/build.zig b/lib/std/build.zig index ad4be9e4ca..53f8f19df5 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1687,7 +1687,9 @@ pub const LibExeObjStep = struct { } pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { - self.link_objects.append(LinkObject{ .AssemblyFile = self.builder.dupe(path) }) catch unreachable; + self.link_objects.append(LinkObject{ + .AssemblyFile = .{ .path = self.builder.dupe(path) }, + }) catch unreachable; } pub fn addAssemblyFileFromWriteFileStep(self: *LibExeObjStep, wfs: *WriteFileStep, basename: []const u8) void { diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index dbd19fbadf..55f044094e 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -205,6 +205,7 @@ pub const TypeInfo = union(enum) { name: []const u8, offset: ?comptime_int, field_type: type, + default_value: var, }; /// This data structure is used by the Zig language code generation and @@ -253,6 +254,7 @@ pub const TypeInfo = union(enum) { tag_type: type, fields: []EnumField, decls: []Declaration, + is_exhaustive: bool, }; /// This data structure is used by the Zig language code generation and diff --git a/lib/std/c.zig b/lib/std/c.zig index 45d0b4db03..c912c72418 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -185,7 +185,7 @@ pub extern "c" fn getaddrinfo( noalias service: [*:0]const u8, noalias hints: *const addrinfo, noalias res: **addrinfo, -) c_int; +) EAI; pub extern "c" fn freeaddrinfo(res: *addrinfo) void; @@ -197,9 +197,9 @@ pub extern "c" fn getnameinfo( noalias serv: [*]u8, servlen: socklen_t, flags: u32, -) c_int; +) EAI; -pub extern "c" fn gai_strerror(errcode: c_int) [*:0]const u8; +pub extern "c" fn gai_strerror(errcode: EAI) [*:0]const u8; pub extern "c" fn poll(fds: [*]pollfd, nfds: nfds_t, timeout: c_int) c_int; diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index bcb5a38ba3..bc58b1fba1 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -70,47 +70,52 @@ pub const AI_NUMERICHOST = 0x00000004; /// prevent service name resolution pub const AI_NUMERICSERV = 0x00001000; -/// address family for hostname not supported -pub const EAI_ADDRFAMILY = 1; +pub const EAI = extern enum(c_int) { + /// address family for hostname not supported + ADDRFAMILY = 1, -/// temporary failure in name resolution -pub const EAI_AGAIN = 2; + /// temporary failure in name resolution + AGAIN = 2, -/// invalid value for ai_flags -pub const EAI_BADFLAGS = 3; + /// invalid value for ai_flags + BADFLAGS = 3, -/// non-recoverable failure in name resolution -pub const EAI_FAIL = 4; + /// non-recoverable failure in name resolution + FAIL = 4, -/// ai_family not supported -pub const EAI_FAMILY = 5; + /// ai_family not supported + FAMILY = 5, -/// memory allocation failure -pub const EAI_MEMORY = 6; + /// memory allocation failure + MEMORY = 6, -/// no address associated with hostname -pub const EAI_NODATA = 7; + /// no address associated with hostname + NODATA = 7, -/// hostname nor servname provided, or not known -pub const EAI_NONAME = 8; + /// hostname nor servname provided, or not known + NONAME = 8, -/// servname not supported for ai_socktype -pub const EAI_SERVICE = 9; + /// servname not supported for ai_socktype + SERVICE = 9, -/// ai_socktype not supported -pub const EAI_SOCKTYPE = 10; + /// ai_socktype not supported + SOCKTYPE = 10, -/// system error returned in errno -pub const EAI_SYSTEM = 11; + /// system error returned in errno + SYSTEM = 11, -/// invalid value for hints -pub const EAI_BADHINTS = 12; + /// invalid value for hints + BADHINTS = 12, -/// resolved protocol is unknown -pub const EAI_PROTOCOL = 13; + /// resolved protocol is unknown + PROTOCOL = 13, + + /// argument buffer overflow + OVERFLOW = 14, + + _, +}; -/// argument buffer overflow -pub const EAI_OVERFLOW = 14; pub const EAI_MAX = 15; pub const pthread_mutex_t = extern struct { diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 95e27a7d92..4c6614c978 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -23,47 +23,51 @@ pub const pthread_attr_t = extern struct { __align: c_long, }; -/// address family for hostname not supported -pub const EAI_ADDRFAMILY = 1; +pub const EAI = extern enum(c_int) { + /// address family for hostname not supported + ADDRFAMILY = 1, -/// name could not be resolved at this time -pub const EAI_AGAIN = 2; + /// name could not be resolved at this time + AGAIN = 2, -/// flags parameter had an invalid value -pub const EAI_BADFLAGS = 3; + /// flags parameter had an invalid value + BADFLAGS = 3, -/// non-recoverable failure in name resolution -pub const EAI_FAIL = 4; + /// non-recoverable failure in name resolution + FAIL = 4, -/// address family not recognized -pub const EAI_FAMILY = 5; + /// address family not recognized + FAMILY = 5, -/// memory allocation failure -pub const EAI_MEMORY = 6; + /// memory allocation failure + MEMORY = 6, -/// no address associated with hostname -pub const EAI_NODATA = 7; + /// no address associated with hostname + NODATA = 7, -/// name does not resolve -pub const EAI_NONAME = 8; + /// name does not resolve + NONAME = 8, -/// service not recognized for socket type -pub const EAI_SERVICE = 9; + /// service not recognized for socket type + SERVICE = 9, -/// intended socket type was not recognized -pub const EAI_SOCKTYPE = 10; + /// intended socket type was not recognized + SOCKTYPE = 10, -/// system error returned in errno -pub const EAI_SYSTEM = 11; + /// system error returned in errno + SYSTEM = 11, -/// invalid value for hints -pub const EAI_BADHINTS = 12; + /// invalid value for hints + BADHINTS = 12, -/// resolved protocol is unknown -pub const EAI_PROTOCOL = 13; + /// resolved protocol is unknown + PROTOCOL = 13, -/// argument buffer overflow -pub const EAI_OVERFLOW = 14; + /// argument buffer overflow + OVERFLOW = 14, + + _, +}; pub const EAI_MAX = 15; diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index 9969474097..0f7abaaaa0 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -32,25 +32,29 @@ pub const NI_NAMEREQD = 0x08; pub const NI_DGRAM = 0x10; pub const NI_NUMERICSCOPE = 0x100; -pub const EAI_BADFLAGS = -1; -pub const EAI_NONAME = -2; -pub const EAI_AGAIN = -3; -pub const EAI_FAIL = -4; -pub const EAI_FAMILY = -6; -pub const EAI_SOCKTYPE = -7; -pub const EAI_SERVICE = -8; -pub const EAI_MEMORY = -10; -pub const EAI_SYSTEM = -11; -pub const EAI_OVERFLOW = -12; +pub const EAI = extern enum(c_int) { + BADFLAGS = -1, + NONAME = -2, + AGAIN = -3, + FAIL = -4, + FAMILY = -6, + SOCKTYPE = -7, + SERVICE = -8, + MEMORY = -10, + SYSTEM = -11, + OVERFLOW = -12, -pub const EAI_NODATA = -5; -pub const EAI_ADDRFAMILY = -9; -pub const EAI_INPROGRESS = -100; -pub const EAI_CANCELED = -101; -pub const EAI_NOTCANCELED = -102; -pub const EAI_ALLDONE = -103; -pub const EAI_INTR = -104; -pub const EAI_IDN_ENCODE = -105; + NODATA = -5, + ADDRFAMILY = -9, + INPROGRESS = -100, + CANCELED = -101, + NOTCANCELED = -102, + ALLDONE = -103, + INTR = -104, + IDN_ENCODE = -105, + + _, +}; pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int; diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index db912ec922..ef64870b8d 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -277,7 +277,7 @@ pub const WindowsDynLib = struct { } pub fn openC(path_c: [*:0]const u8) !WindowsDynLib { - const path_w = try windows.cStrToPrefixedFileW(path); + const path_w = try windows.cStrToPrefixedFileW(path_c); return openW(&path_w); } diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 548ef8ccce..5a650c3b10 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -431,7 +431,7 @@ pub fn formatType( }, else => return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }), }, - .Many => { + .Many, .C => { if (ptr_info.child == u8) { if (fmt.len > 0 and fmt[0] == 's') { const len = mem.len(u8, value); @@ -449,9 +449,6 @@ pub fn formatType( } return format(context, Errors, output, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) }); }, - .C => { - return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); - }, }, .Array => |info| { const Slice = @Type(builtin.TypeInfo{ @@ -1285,8 +1282,16 @@ test "pointer" { } test "cstr" { - try testFmt("cstr: Test C\n", "cstr: {s}\n", .{"Test C"}); - try testFmt("cstr: Test C \n", "cstr: {s:10}\n", .{"Test C"}); + try testFmt( + "cstr: Test C\n", + "cstr: {s}\n", + .{@ptrCast([*c]const u8, "Test C")}, + ); + try testFmt( + "cstr: Test C \n", + "cstr: {s:10}\n", + .{@ptrCast([*c]const u8, "Test C")}, + ); } test "filesize" { diff --git a/lib/std/hash/benchmark.zig b/lib/std/hash/benchmark.zig index ce9ed75b58..c792013e06 100644 --- a/lib/std/hash/benchmark.zig +++ b/lib/std/hash/benchmark.zig @@ -47,11 +47,11 @@ const hashes = [_]Hash{ .name = "adler32", }, Hash{ - .ty = hash.crc.Crc32WithPoly(hash.crc.Polynomial.IEEE), + .ty = hash.crc.Crc32WithPoly(.IEEE), .name = "crc32-slicing-by-8", }, Hash{ - .ty = hash.crc.Crc32SmallWithPoly(hash.crc.Polynomial.IEEE), + .ty = hash.crc.Crc32SmallWithPoly(.IEEE), .name = "crc32-half-byte-lookup", }, Hash{ diff --git a/lib/std/hash/crc.zig b/lib/std/hash/crc.zig index 6176ded81d..506d8c8aed 100644 --- a/lib/std/hash/crc.zig +++ b/lib/std/hash/crc.zig @@ -9,17 +9,18 @@ const std = @import("../std.zig"); const debug = std.debug; const testing = std.testing; -pub const Polynomial = struct { - pub const IEEE = 0xedb88320; - pub const Castagnoli = 0x82f63b78; - pub const Koopman = 0xeb31d82e; +pub const Polynomial = enum(u32) { + IEEE = 0xedb88320, + Castagnoli = 0x82f63b78, + Koopman = 0xeb31d82e, + _, }; // IEEE is by far the most common CRC and so is aliased by default. -pub const Crc32 = Crc32WithPoly(Polynomial.IEEE); +pub const Crc32 = Crc32WithPoly(.IEEE); // slicing-by-8 crc32 implementation. -pub fn Crc32WithPoly(comptime poly: u32) type { +pub fn Crc32WithPoly(comptime poly: Polynomial) type { return struct { const Self = @This(); const lookup_tables = comptime block: { @@ -31,7 +32,7 @@ pub fn Crc32WithPoly(comptime poly: u32) type { var j: usize = 0; while (j < 8) : (j += 1) { if (crc & 1 == 1) { - crc = (crc >> 1) ^ poly; + crc = (crc >> 1) ^ @enumToInt(poly); } else { crc = (crc >> 1); } @@ -100,7 +101,7 @@ pub fn Crc32WithPoly(comptime poly: u32) type { } test "crc32 ieee" { - const Crc32Ieee = Crc32WithPoly(Polynomial.IEEE); + const Crc32Ieee = Crc32WithPoly(.IEEE); testing.expect(Crc32Ieee.hash("") == 0x00000000); testing.expect(Crc32Ieee.hash("a") == 0xe8b7be43); @@ -108,7 +109,7 @@ test "crc32 ieee" { } test "crc32 castagnoli" { - const Crc32Castagnoli = Crc32WithPoly(Polynomial.Castagnoli); + const Crc32Castagnoli = Crc32WithPoly(.Castagnoli); testing.expect(Crc32Castagnoli.hash("") == 0x00000000); testing.expect(Crc32Castagnoli.hash("a") == 0xc1d04330); @@ -116,7 +117,7 @@ test "crc32 castagnoli" { } // half-byte lookup table implementation. -pub fn Crc32SmallWithPoly(comptime poly: u32) type { +pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type { return struct { const Self = @This(); const lookup_table = comptime block: { @@ -127,7 +128,7 @@ pub fn Crc32SmallWithPoly(comptime poly: u32) type { var j: usize = 0; while (j < 8) : (j += 1) { if (crc & 1 == 1) { - crc = (crc >> 1) ^ poly; + crc = (crc >> 1) ^ @enumToInt(poly); } else { crc = (crc >> 1); } @@ -164,7 +165,7 @@ pub fn Crc32SmallWithPoly(comptime poly: u32) type { } test "small crc32 ieee" { - const Crc32Ieee = Crc32SmallWithPoly(Polynomial.IEEE); + const Crc32Ieee = Crc32SmallWithPoly(.IEEE); testing.expect(Crc32Ieee.hash("") == 0x00000000); testing.expect(Crc32Ieee.hash("a") == 0xe8b7be43); @@ -172,7 +173,7 @@ test "small crc32 ieee" { } test "small crc32 castagnoli" { - const Crc32Castagnoli = Crc32SmallWithPoly(Polynomial.Castagnoli); + const Crc32Castagnoli = Crc32SmallWithPoly(.Castagnoli); testing.expect(Crc32Castagnoli.hash("") == 0x00000000); testing.expect(Crc32Castagnoli.hash("a") == 0xc1d04330); diff --git a/lib/std/net.zig b/lib/std/net.zig index 47ce95c99f..c113462855 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -452,18 +452,18 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* }; var res: *os.addrinfo = undefined; switch (os.system.getaddrinfo(name_c.ptr, @ptrCast([*:0]const u8, port_c.ptr), &hints, &res)) { - 0 => {}, - c.EAI_ADDRFAMILY => return error.HostLacksNetworkAddresses, - c.EAI_AGAIN => return error.TemporaryNameServerFailure, - c.EAI_BADFLAGS => unreachable, // Invalid hints - c.EAI_FAIL => return error.NameServerFailure, - c.EAI_FAMILY => return error.AddressFamilyNotSupported, - c.EAI_MEMORY => return error.OutOfMemory, - c.EAI_NODATA => return error.HostLacksNetworkAddresses, - c.EAI_NONAME => return error.UnknownHostName, - c.EAI_SERVICE => return error.ServiceUnavailable, - c.EAI_SOCKTYPE => unreachable, // Invalid socket type requested in hints - c.EAI_SYSTEM => switch (os.errno(-1)) { + @intToEnum(os.system.EAI, 0) => {}, + .ADDRFAMILY => return error.HostLacksNetworkAddresses, + .AGAIN => return error.TemporaryNameServerFailure, + .BADFLAGS => unreachable, // Invalid hints + .FAIL => return error.NameServerFailure, + .FAMILY => return error.AddressFamilyNotSupported, + .MEMORY => return error.OutOfMemory, + .NODATA => return error.HostLacksNetworkAddresses, + .NONAME => return error.UnknownHostName, + .SERVICE => return error.ServiceUnavailable, + .SOCKTYPE => unreachable, // Invalid socket type requested in hints + .SYSTEM => switch (os.errno(-1)) { else => |e| return os.unexpectedErrno(e), }, else => unreachable, diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 51ea49005e..f4024e1f1d 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -18,7 +18,7 @@ pub usingnamespace switch (builtin.arch) { else => struct {}, }; -const is_mips = builtin.arch == .mipsel; +const is_mips = builtin.arch.isMIPS(); pub const pid_t = i32; pub const fd_t = i32; diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 2128883545..90dbf0cdf4 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -16,42 +16,42 @@ comptime { else => {}, } - @export(@import("compiler_rt/comparesf2.zig").__lesf2, .{ .name = "__lesf2", .linkage = linkage }); - @export(@import("compiler_rt/comparedf2.zig").__ledf2, .{ .name = "__ledf2", .linkage = linkage }); - @export(@import("compiler_rt/comparetf2.zig").__letf2, .{ .name = "__letf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__lesf2, .{ .name = "__lesf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__ledf2, .{ .name = "__ledf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__letf2, .{ .name = "__letf2", .linkage = linkage }); - @export(@import("compiler_rt/comparesf2.zig").__gesf2, .{ .name = "__gesf2", .linkage = linkage }); - @export(@import("compiler_rt/comparedf2.zig").__gedf2, .{ .name = "__gedf2", .linkage = linkage }); - @export(@import("compiler_rt/comparetf2.zig").__getf2, .{ .name = "__getf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__gesf2, .{ .name = "__gesf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__gedf2, .{ .name = "__gedf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__getf2, .{ .name = "__getf2", .linkage = linkage }); if (!is_test) { - @export(@import("compiler_rt/comparesf2.zig").__lesf2, .{ .name = "__cmpsf2", .linkage = linkage }); - @export(@import("compiler_rt/comparedf2.zig").__ledf2, .{ .name = "__cmpdf2", .linkage = linkage }); - @export(@import("compiler_rt/comparetf2.zig").__letf2, .{ .name = "__cmptf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__lesf2, .{ .name = "__cmpsf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__ledf2, .{ .name = "__cmpdf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__letf2, .{ .name = "__cmptf2", .linkage = linkage }); - @export(@import("compiler_rt/comparesf2.zig").__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); - @export(@import("compiler_rt/comparedf2.zig").__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); - @export(@import("compiler_rt/comparetf2.zig").__letf2, .{ .name = "__eqtf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__letf2, .{ .name = "__eqtf2", .linkage = linkage }); - @export(@import("compiler_rt/comparesf2.zig").__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); - @export(@import("compiler_rt/comparedf2.zig").__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); - @export(@import("compiler_rt/comparetf2.zig").__letf2, .{ .name = "__lttf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__letf2, .{ .name = "__lttf2", .linkage = linkage }); - @export(@import("compiler_rt/comparesf2.zig").__nesf2, .{ .name = "__nesf2", .linkage = linkage }); - @export(@import("compiler_rt/comparedf2.zig").__nedf2, .{ .name = "__nedf2", .linkage = linkage }); - @export(@import("compiler_rt/comparetf2.zig").__letf2, .{ .name = "__netf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__nesf2, .{ .name = "__nesf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__nedf2, .{ .name = "__nedf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__letf2, .{ .name = "__netf2", .linkage = linkage }); - @export(@import("compiler_rt/comparesf2.zig").__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); - @export(@import("compiler_rt/comparedf2.zig").__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); - @export(@import("compiler_rt/comparetf2.zig").__getf2, .{ .name = "__gttf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__getf2, .{ .name = "__gttf2", .linkage = linkage }); @export(@import("compiler_rt/extendXfYf2.zig").__extendhfsf2, .{ .name = "__gnu_h2f_ieee", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__truncsfhf2, .{ .name = "__gnu_f2h_ieee", .linkage = linkage }); } - @export(@import("compiler_rt/comparesf2.zig").__unordsf2, .{ .name = "__unordsf2", .linkage = linkage }); - @export(@import("compiler_rt/comparedf2.zig").__unorddf2, .{ .name = "__unorddf2", .linkage = linkage }); - @export(@import("compiler_rt/comparetf2.zig").__unordtf2, .{ .name = "__unordtf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__unordsf2, .{ .name = "__unordsf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__unorddf2, .{ .name = "__unorddf2", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__unordtf2, .{ .name = "__unordtf2", .linkage = linkage }); @export(@import("compiler_rt/addXf3.zig").__addsf3, .{ .name = "__addsf3", .linkage = linkage }); @export(@import("compiler_rt/addXf3.zig").__adddf3, .{ .name = "__adddf3", .linkage = linkage }); @@ -146,8 +146,10 @@ comptime { @export(@import("compiler_rt/negXf2.zig").__negsf2, .{ .name = "__negsf2", .linkage = linkage }); @export(@import("compiler_rt/negXf2.zig").__negdf2, .{ .name = "__negdf2", .linkage = linkage }); - if (is_arm_arch and !is_arm_64 and !is_test) { - @export(@import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = strong_linkage }); + @export(@import("compiler_rt/clzsi2.zig").__clzsi2, .{ .name = "__clzsi2", .linkage = linkage }); + + if (builtin.arch.isARM() and !is_test) { + @export(@import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage }); @export(@import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage }); @export(@import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage }); @@ -177,7 +179,9 @@ comptime { @export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage }); @export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage }); - @export(@import("compiler_rt/arm.zig").__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); + if (builtin.os == .linux) { + @export(@import("compiler_rt/arm.zig").__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); + } @export(@import("compiler_rt/extendXfYf2.zig").__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage }); @export(@import("compiler_rt/floatsiXf.zig").__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage }); @@ -222,20 +226,29 @@ comptime { @export(@import("compiler_rt/divsf3.zig").__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = linkage }); @export(@import("compiler_rt/divdf3.zig").__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = linkage }); - @export(@import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage }); - @export(@import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage }); - @export(@import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage }); - @export(@import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage }); - @export(@import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage }); - @export(@import("compiler_rt/comparesf2.zig").__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage }); - @export(@import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage }); - @export(@import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage }); - @export(@import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage }); - @export(@import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage }); - @export(@import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage }); - @export(@import("compiler_rt/comparedf2.zig").__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage }); + @export(@import("compiler_rt/compareXf2.zig").__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage }); } + + if (builtin.arch == .i386 and builtin.abi == .msvc) { + // Don't let LLVM apply the stdcall name mangling on those MSVC builtins + @export(@import("compiler_rt/aulldiv.zig")._alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage }); + @export(@import("compiler_rt/aulldiv.zig")._aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage }); + @export(@import("compiler_rt/aullrem.zig")._allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage }); + @export(@import("compiler_rt/aullrem.zig")._aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); + } + if (builtin.os == .windows) { // Default stack-probe functions emitted by LLVM if (is_mingw) { @@ -254,13 +267,6 @@ comptime { switch (builtin.arch) { .i386 => { - // Don't let LLVM apply the stdcall name mangling on those MSVC - // builtin functions - @export(@import("compiler_rt/aulldiv.zig")._alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage }); - @export(@import("compiler_rt/aulldiv.zig")._aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage }); - @export(@import("compiler_rt/aullrem.zig")._allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage }); - @export(@import("compiler_rt/aullrem.zig")._aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); - @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage }); @export(@import("compiler_rt/modti3.zig").__modti3, .{ .name = "__modti3", .linkage = linkage }); @export(@import("compiler_rt/multi3.zig").__multi3, .{ .name = "__multi3", .linkage = linkage }); @@ -295,16 +301,12 @@ comptime { @export(@import("compiler_rt/mulodi4.zig").__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); } -const std = @import("std"); -const assert = std.debug.assert; -const testing = std.testing; - // 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: ?*builtin.StackTrace) noreturn { @setCold(true); if (is_test) { - std.debug.panic("{}", .{msg}); + @import("std").debug.panic("{}", .{msg}); } else { unreachable; } @@ -320,23 +322,3 @@ extern var __stack_chk_guard: usize = blk: { buf[@sizeOf(usize) - 2] = '\n'; break :blk @bitCast(usize, buf); }; - -const is_arm_64 = switch (builtin.arch) { - builtin.Arch.aarch64, - builtin.Arch.aarch64_be, - => true, - else => false, -}; - -const is_arm_arch = switch (builtin.arch) { - builtin.Arch.arm, - builtin.Arch.armeb, - builtin.Arch.aarch64, - builtin.Arch.aarch64_be, - builtin.Arch.thumb, - builtin.Arch.thumbeb, - => true, - else => false, -}; - -const is_arm_32 = is_arm_arch and !is_arm_64; diff --git a/lib/std/special/compiler_rt/arm.zig b/lib/std/special/compiler_rt/arm.zig index 9ba423931b..5e718ed4c4 100644 --- a/lib/std/special/compiler_rt/arm.zig +++ b/lib/std/special/compiler_rt/arm.zig @@ -1,6 +1,5 @@ // ARM specific builtins const builtin = @import("builtin"); -const is_test = builtin.is_test; const __divmodsi4 = @import("int.zig").__divmodsi4; const __udivmodsi4 = @import("int.zig").__udivmodsi4; @@ -33,18 +32,14 @@ pub fn __aeabi_memclr(dest: [*]u8, n: usize) callconv(.AAPCS) void { _ = memset(dest, 0, n); } -pub fn __aeabi_unwind_cpp_pr0() callconv(.C) void { - unreachable; -} -pub fn __aeabi_unwind_cpp_pr1() callconv(.C) void { - unreachable; -} -pub fn __aeabi_unwind_cpp_pr2() callconv(.C) void { - unreachable; -} +// 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 {} // This function can only clobber r0 according to the ABI pub fn __aeabi_read_tp() callconv(.Naked) void { + @setRuntimeSafety(false); asm volatile ( \\ mrc p15, 0, r0, c13, c0, 3 \\ bx lr @@ -56,6 +51,7 @@ pub fn __aeabi_read_tp() callconv(.Naked) void { // calling convention is always respected pub fn __aeabi_uidivmod() callconv(.Naked) void { + @setRuntimeSafety(false); // Divide r0 by r1; the quotient goes in r0, the remainder in r1 asm volatile ( \\ push {lr} @@ -73,6 +69,7 @@ pub fn __aeabi_uidivmod() callconv(.Naked) void { } pub fn __aeabi_uldivmod() callconv(.Naked) void { + @setRuntimeSafety(false); // Divide r1:r0 by r3:r2; the quotient goes in r1:r0, the remainder in r3:r2 asm volatile ( \\ push {r4, lr} @@ -92,6 +89,7 @@ pub fn __aeabi_uldivmod() callconv(.Naked) void { } pub fn __aeabi_idivmod() callconv(.Naked) void { + @setRuntimeSafety(false); // Divide r0 by r1; the quotient goes in r0, the remainder in r1 asm volatile ( \\ push {lr} @@ -109,6 +107,7 @@ pub fn __aeabi_idivmod() callconv(.Naked) void { } pub fn __aeabi_ldivmod() callconv(.Naked) void { + @setRuntimeSafety(false); // Divide r1:r0 by r3:r2; the quotient goes in r1:r0, the remainder in r3:r2 asm volatile ( \\ push {r4, lr} diff --git a/lib/std/special/compiler_rt/arm/aeabi_dcmp.zig b/lib/std/special/compiler_rt/arm/aeabi_dcmp.zig deleted file mode 100644 index a8ed182901..0000000000 --- a/lib/std/special/compiler_rt/arm/aeabi_dcmp.zig +++ /dev/null @@ -1,95 +0,0 @@ -// Ported from: -// -// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/arm/aeabi_dcmp.S - -const ConditionalOperator = enum { - Eq, - Lt, - Le, - Ge, - Gt, -}; - -pub fn __aeabi_dcmpeq() callconv(.Naked) noreturn { - @setRuntimeSafety(false); - @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Eq}); - unreachable; -} - -pub fn __aeabi_dcmplt() callconv(.Naked) noreturn { - @setRuntimeSafety(false); - @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Lt}); - unreachable; -} - -pub fn __aeabi_dcmple() callconv(.Naked) noreturn { - @setRuntimeSafety(false); - @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Le}); - unreachable; -} - -pub fn __aeabi_dcmpge() callconv(.Naked) noreturn { - @setRuntimeSafety(false); - @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Ge}); - unreachable; -} - -pub fn __aeabi_dcmpgt() callconv(.Naked) noreturn { - @setRuntimeSafety(false); - @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Gt}); - unreachable; -} - -fn aeabi_dcmp(comptime cond: ConditionalOperator) void { - @setRuntimeSafety(false); - asm volatile ( - \\ push { r4, lr } - ); - - switch (cond) { - .Eq => asm volatile ( - \\ bl __eqdf2 - \\ cmp r0, #0 - \\ beq 1f - \\ movs r0, #0 - \\ pop { r4, pc } - \\ 1: - ), - .Lt => asm volatile ( - \\ bl __ltdf2 - \\ cmp r0, #0 - \\ blt 1f - \\ movs r0, #0 - \\ pop { r4, pc } - \\ 1: - ), - .Le => asm volatile ( - \\ bl __ledf2 - \\ cmp r0, #0 - \\ ble 1f - \\ movs r0, #0 - \\ pop { r4, pc } - \\ 1: - ), - .Ge => asm volatile ( - \\ bl __ltdf2 - \\ cmp r0, #0 - \\ bge 1f - \\ movs r0, #0 - \\ pop { r4, pc } - \\ 1: - ), - .Gt => asm volatile ( - \\ bl __gtdf2 - \\ cmp r0, #0 - \\ bgt 1f - \\ movs r0, #0 - \\ pop { r4, pc } - \\ 1: - ), - } - asm volatile ( - \\ movs r0, #1 - \\ pop { r4, pc } - ); -} diff --git a/lib/std/special/compiler_rt/arm/aeabi_fcmp.zig b/lib/std/special/compiler_rt/arm/aeabi_fcmp.zig deleted file mode 100644 index 0b4c0f0d41..0000000000 --- a/lib/std/special/compiler_rt/arm/aeabi_fcmp.zig +++ /dev/null @@ -1,95 +0,0 @@ -// Ported from: -// -// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/arm/aeabi_fcmp.S - -const ConditionalOperator = enum { - Eq, - Lt, - Le, - Ge, - Gt, -}; - -pub fn __aeabi_fcmpeq() callconv(.Naked) noreturn { - @setRuntimeSafety(false); - @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Eq}); - unreachable; -} - -pub fn __aeabi_fcmplt() callconv(.Naked) noreturn { - @setRuntimeSafety(false); - @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Lt}); - unreachable; -} - -pub fn __aeabi_fcmple() callconv(.Naked) noreturn { - @setRuntimeSafety(false); - @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Le}); - unreachable; -} - -pub fn __aeabi_fcmpge() callconv(.Naked) noreturn { - @setRuntimeSafety(false); - @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Ge}); - unreachable; -} - -pub fn __aeabi_fcmpgt() callconv(.Naked) noreturn { - @setRuntimeSafety(false); - @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Gt}); - unreachable; -} - -fn aeabi_fcmp(comptime cond: ConditionalOperator) void { - @setRuntimeSafety(false); - asm volatile ( - \\ push { r4, lr } - ); - - switch (cond) { - .Eq => asm volatile ( - \\ bl __eqsf2 - \\ cmp r0, #0 - \\ beq 1f - \\ movs r0, #0 - \\ pop { r4, pc } - \\ 1: - ), - .Lt => asm volatile ( - \\ bl __ltsf2 - \\ cmp r0, #0 - \\ blt 1f - \\ movs r0, #0 - \\ pop { r4, pc } - \\ 1: - ), - .Le => asm volatile ( - \\ bl __lesf2 - \\ cmp r0, #0 - \\ ble 1f - \\ movs r0, #0 - \\ pop { r4, pc } - \\ 1: - ), - .Ge => asm volatile ( - \\ bl __ltsf2 - \\ cmp r0, #0 - \\ bge 1f - \\ movs r0, #0 - \\ pop { r4, pc } - \\ 1: - ), - .Gt => asm volatile ( - \\ bl __gtsf2 - \\ cmp r0, #0 - \\ bgt 1f - \\ movs r0, #0 - \\ pop { r4, pc } - \\ 1: - ), - } - asm volatile ( - \\ movs r0, #1 - \\ pop { r4, pc } - ); -} diff --git a/lib/std/special/compiler_rt/clzsi2.zig b/lib/std/special/compiler_rt/clzsi2.zig new file mode 100644 index 0000000000..6a69ae75f1 --- /dev/null +++ b/lib/std/special/compiler_rt/clzsi2.zig @@ -0,0 +1,116 @@ +const builtin = @import("builtin"); + +fn __clzsi2_generic(a: i32) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + + var x = @bitCast(u32, a); + var n: i32 = 32; + + // Count first bit set using binary search, from Hacker's Delight + var y: u32 = 0; + inline for ([_]i32{ 16, 8, 4, 2, 1 }) |shift| { + y = x >> shift; + if (y != 0) { + n = n - shift; + x = y; + } + } + + return n - @bitCast(i32, x); +} + +fn __clzsi2_thumb1() callconv(.Naked) void { + @setRuntimeSafety(builtin.is_test); + + // Similar to the generic version with the last two rounds replaced by a LUT + asm volatile ( + \\ movs r1, #32 + \\ lsrs r2, r0, #16 + \\ beq 1f + \\ subs r1, #16 + \\ movs r0, r2 + \\ 1: + \\ lsrs r2, r0, #8 + \\ beq 1f + \\ subs r1, #8 + \\ movs r0, r2 + \\ 1: + \\ lsrs r2, r0, #4 + \\ beq 1f + \\ subs r1, #4 + \\ movs r0, r2 + \\ 1: + \\ ldr r3, =LUT + \\ ldrb r0, [r3, r0] + \\ subs r0, r1, r0 + \\ bx lr + \\ .p2align 2 + \\ LUT: + \\ .byte 4,3,2,2,1,1,1,1,0,0,0,0,0,0,0,0 + ); + + unreachable; +} + +fn __clzsi2_arm32() callconv(.Naked) void { + @setRuntimeSafety(builtin.is_test); + + asm volatile ( + \\ // Assumption: n != 0 + \\ // r0: n + \\ // r1: count of leading zeros in n + 1 + \\ // r2: scratch register for shifted r0 + \\ mov r1, #1 + \\ + \\ // Basic block: + \\ // if ((r0 >> SHIFT) == 0) + \\ // r1 += SHIFT; + \\ // else + \\ // r0 >>= SHIFT; + \\ // for descending powers of two as SHIFT. + \\ lsrs r2, r0, #16 + \\ movne r0, r2 + \\ addeq r1, #16 + \\ + \\ lsrs r2, r0, #8 + \\ movne r0, r2 + \\ addeq r1, #8 + \\ + \\ lsrs r2, r0, #4 + \\ movne r0, r2 + \\ addeq r1, #4 + \\ + \\ lsrs r2, r0, #2 + \\ movne r0, r2 + \\ addeq r1, #2 + \\ + \\ // The basic block invariants at this point are (r0 >> 2) == 0 and + \\ // r0 != 0. This means 1 <= r0 <= 3 and 0 <= (r0 >> 1) <= 1. + \\ // + \\ // r0 | (r0 >> 1) == 0 | (r0 >> 1) == 1 | -(r0 >> 1) | 1 - (r0 >> 1)f + \\ // ---+----------------+----------------+------------+-------------- + \\ // 1 | 1 | 0 | 0 | 1 + \\ // 2 | 0 | 1 | -1 | 0 + \\ // 3 | 0 | 1 | -1 | 0 + \\ // + \\ // The r1's initial value of 1 compensates for the 1 here. + \\ sub r0, r1, r0, lsr #1 + \\ bx lr + ); + + unreachable; +} + +pub const __clzsi2 = blk: { + if (builtin.arch.isARM()) { + break :blk __clzsi2_arm32; + } else if (builtin.arch.isThumb()) { + break :blk __clzsi2_thumb1; + } else { + break :blk __clzsi2_generic; + } +}; + +test "test clzsi2" { + _ = @import("clzsi2_test.zig"); +} diff --git a/lib/std/special/compiler_rt/clzsi2_test.zig b/lib/std/special/compiler_rt/clzsi2_test.zig new file mode 100644 index 0000000000..ff94455846 --- /dev/null +++ b/lib/std/special/compiler_rt/clzsi2_test.zig @@ -0,0 +1,292 @@ +const clzsi2 = @import("clzsi2.zig"); +const testing = @import("std").testing; + +fn test__clzsi2(a: u32, expected: i32) void { + var nakedClzsi2 = clzsi2.__clzsi2; + var actualClzsi2 = @ptrCast(fn (a: i32) callconv(.C) i32, nakedClzsi2); + var x = @intCast(i32, a); + var result = actualClzsi2(x); + testing.expectEqual(expected, result); +} + +test "clzsi2" { + test__clzsi2(0x00800000, 8); + test__clzsi2(0x01000000, 7); + test__clzsi2(0x02000000, 6); + test__clzsi2(0x03000000, 6); + test__clzsi2(0x04000000, 5); + test__clzsi2(0x05000000, 5); + test__clzsi2(0x06000000, 5); + test__clzsi2(0x07000000, 5); + test__clzsi2(0x08000000, 4); + test__clzsi2(0x09000000, 4); + test__clzsi2(0x0A000000, 4); + test__clzsi2(0x0B000000, 4); + test__clzsi2(0x0C000000, 4); + test__clzsi2(0x0D000000, 4); + test__clzsi2(0x0E000000, 4); + test__clzsi2(0x0F000000, 4); + test__clzsi2(0x10000000, 3); + test__clzsi2(0x11000000, 3); + test__clzsi2(0x12000000, 3); + test__clzsi2(0x13000000, 3); + test__clzsi2(0x14000000, 3); + test__clzsi2(0x15000000, 3); + test__clzsi2(0x16000000, 3); + test__clzsi2(0x17000000, 3); + test__clzsi2(0x18000000, 3); + test__clzsi2(0x19000000, 3); + test__clzsi2(0x1A000000, 3); + test__clzsi2(0x1B000000, 3); + test__clzsi2(0x1C000000, 3); + test__clzsi2(0x1D000000, 3); + test__clzsi2(0x1E000000, 3); + test__clzsi2(0x1F000000, 3); + test__clzsi2(0x20000000, 2); + test__clzsi2(0x21000000, 2); + test__clzsi2(0x22000000, 2); + test__clzsi2(0x23000000, 2); + test__clzsi2(0x24000000, 2); + test__clzsi2(0x25000000, 2); + test__clzsi2(0x26000000, 2); + test__clzsi2(0x27000000, 2); + test__clzsi2(0x28000000, 2); + test__clzsi2(0x29000000, 2); + test__clzsi2(0x2A000000, 2); + test__clzsi2(0x2B000000, 2); + test__clzsi2(0x2C000000, 2); + test__clzsi2(0x2D000000, 2); + test__clzsi2(0x2E000000, 2); + test__clzsi2(0x2F000000, 2); + test__clzsi2(0x30000000, 2); + test__clzsi2(0x31000000, 2); + test__clzsi2(0x32000000, 2); + test__clzsi2(0x33000000, 2); + test__clzsi2(0x34000000, 2); + test__clzsi2(0x35000000, 2); + test__clzsi2(0x36000000, 2); + test__clzsi2(0x37000000, 2); + test__clzsi2(0x38000000, 2); + test__clzsi2(0x39000000, 2); + test__clzsi2(0x3A000000, 2); + test__clzsi2(0x3B000000, 2); + test__clzsi2(0x3C000000, 2); + test__clzsi2(0x3D000000, 2); + test__clzsi2(0x3E000000, 2); + test__clzsi2(0x3F000000, 2); + test__clzsi2(0x40000000, 1); + test__clzsi2(0x41000000, 1); + test__clzsi2(0x42000000, 1); + test__clzsi2(0x43000000, 1); + test__clzsi2(0x44000000, 1); + test__clzsi2(0x45000000, 1); + test__clzsi2(0x46000000, 1); + test__clzsi2(0x47000000, 1); + test__clzsi2(0x48000000, 1); + test__clzsi2(0x49000000, 1); + test__clzsi2(0x4A000000, 1); + test__clzsi2(0x4B000000, 1); + test__clzsi2(0x4C000000, 1); + test__clzsi2(0x4D000000, 1); + test__clzsi2(0x4E000000, 1); + test__clzsi2(0x4F000000, 1); + test__clzsi2(0x50000000, 1); + test__clzsi2(0x51000000, 1); + test__clzsi2(0x52000000, 1); + test__clzsi2(0x53000000, 1); + test__clzsi2(0x54000000, 1); + test__clzsi2(0x55000000, 1); + test__clzsi2(0x56000000, 1); + test__clzsi2(0x57000000, 1); + test__clzsi2(0x58000000, 1); + test__clzsi2(0x59000000, 1); + test__clzsi2(0x5A000000, 1); + test__clzsi2(0x5B000000, 1); + test__clzsi2(0x5C000000, 1); + test__clzsi2(0x5D000000, 1); + test__clzsi2(0x5E000000, 1); + test__clzsi2(0x5F000000, 1); + test__clzsi2(0x60000000, 1); + test__clzsi2(0x61000000, 1); + test__clzsi2(0x62000000, 1); + test__clzsi2(0x63000000, 1); + test__clzsi2(0x64000000, 1); + test__clzsi2(0x65000000, 1); + test__clzsi2(0x66000000, 1); + test__clzsi2(0x67000000, 1); + test__clzsi2(0x68000000, 1); + test__clzsi2(0x69000000, 1); + test__clzsi2(0x6A000000, 1); + test__clzsi2(0x6B000000, 1); + test__clzsi2(0x6C000000, 1); + test__clzsi2(0x6D000000, 1); + test__clzsi2(0x6E000000, 1); + test__clzsi2(0x6F000000, 1); + test__clzsi2(0x70000000, 1); + test__clzsi2(0x71000000, 1); + test__clzsi2(0x72000000, 1); + test__clzsi2(0x73000000, 1); + test__clzsi2(0x74000000, 1); + test__clzsi2(0x75000000, 1); + test__clzsi2(0x76000000, 1); + test__clzsi2(0x77000000, 1); + test__clzsi2(0x78000000, 1); + test__clzsi2(0x79000000, 1); + test__clzsi2(0x7A000000, 1); + test__clzsi2(0x7B000000, 1); + test__clzsi2(0x7C000000, 1); + test__clzsi2(0x7D000000, 1); + test__clzsi2(0x7E000000, 1); + test__clzsi2(0x7F000000, 1); + test__clzsi2(0x80000000, 0); + test__clzsi2(0x81000000, 0); + test__clzsi2(0x82000000, 0); + test__clzsi2(0x83000000, 0); + test__clzsi2(0x84000000, 0); + test__clzsi2(0x85000000, 0); + test__clzsi2(0x86000000, 0); + test__clzsi2(0x87000000, 0); + test__clzsi2(0x88000000, 0); + test__clzsi2(0x89000000, 0); + test__clzsi2(0x8A000000, 0); + test__clzsi2(0x8B000000, 0); + test__clzsi2(0x8C000000, 0); + test__clzsi2(0x8D000000, 0); + test__clzsi2(0x8E000000, 0); + test__clzsi2(0x8F000000, 0); + test__clzsi2(0x90000000, 0); + test__clzsi2(0x91000000, 0); + test__clzsi2(0x92000000, 0); + test__clzsi2(0x93000000, 0); + test__clzsi2(0x94000000, 0); + test__clzsi2(0x95000000, 0); + test__clzsi2(0x96000000, 0); + test__clzsi2(0x97000000, 0); + test__clzsi2(0x98000000, 0); + test__clzsi2(0x99000000, 0); + test__clzsi2(0x9A000000, 0); + test__clzsi2(0x9B000000, 0); + test__clzsi2(0x9C000000, 0); + test__clzsi2(0x9D000000, 0); + test__clzsi2(0x9E000000, 0); + test__clzsi2(0x9F000000, 0); + test__clzsi2(0xA0000000, 0); + test__clzsi2(0xA1000000, 0); + test__clzsi2(0xA2000000, 0); + test__clzsi2(0xA3000000, 0); + test__clzsi2(0xA4000000, 0); + test__clzsi2(0xA5000000, 0); + test__clzsi2(0xA6000000, 0); + test__clzsi2(0xA7000000, 0); + test__clzsi2(0xA8000000, 0); + test__clzsi2(0xA9000000, 0); + test__clzsi2(0xAA000000, 0); + test__clzsi2(0xAB000000, 0); + test__clzsi2(0xAC000000, 0); + test__clzsi2(0xAD000000, 0); + test__clzsi2(0xAE000000, 0); + test__clzsi2(0xAF000000, 0); + test__clzsi2(0xB0000000, 0); + test__clzsi2(0xB1000000, 0); + test__clzsi2(0xB2000000, 0); + test__clzsi2(0xB3000000, 0); + test__clzsi2(0xB4000000, 0); + test__clzsi2(0xB5000000, 0); + test__clzsi2(0xB6000000, 0); + test__clzsi2(0xB7000000, 0); + test__clzsi2(0xB8000000, 0); + test__clzsi2(0xB9000000, 0); + test__clzsi2(0xBA000000, 0); + test__clzsi2(0xBB000000, 0); + test__clzsi2(0xBC000000, 0); + test__clzsi2(0xBD000000, 0); + test__clzsi2(0xBE000000, 0); + test__clzsi2(0xBF000000, 0); + test__clzsi2(0xC0000000, 0); + test__clzsi2(0xC1000000, 0); + test__clzsi2(0xC2000000, 0); + test__clzsi2(0xC3000000, 0); + test__clzsi2(0xC4000000, 0); + test__clzsi2(0xC5000000, 0); + test__clzsi2(0xC6000000, 0); + test__clzsi2(0xC7000000, 0); + test__clzsi2(0xC8000000, 0); + test__clzsi2(0xC9000000, 0); + test__clzsi2(0xCA000000, 0); + test__clzsi2(0xCB000000, 0); + test__clzsi2(0xCC000000, 0); + test__clzsi2(0xCD000000, 0); + test__clzsi2(0xCE000000, 0); + test__clzsi2(0xCF000000, 0); + test__clzsi2(0xD0000000, 0); + test__clzsi2(0xD1000000, 0); + test__clzsi2(0xD2000000, 0); + test__clzsi2(0xD3000000, 0); + test__clzsi2(0xD4000000, 0); + test__clzsi2(0xD5000000, 0); + test__clzsi2(0xD6000000, 0); + test__clzsi2(0xD7000000, 0); + test__clzsi2(0xD8000000, 0); + test__clzsi2(0xD9000000, 0); + test__clzsi2(0xDA000000, 0); + test__clzsi2(0xDB000000, 0); + test__clzsi2(0xDC000000, 0); + test__clzsi2(0xDD000000, 0); + test__clzsi2(0xDE000000, 0); + test__clzsi2(0xDF000000, 0); + test__clzsi2(0xE0000000, 0); + test__clzsi2(0xE1000000, 0); + test__clzsi2(0xE2000000, 0); + test__clzsi2(0xE3000000, 0); + test__clzsi2(0xE4000000, 0); + test__clzsi2(0xE5000000, 0); + test__clzsi2(0xE6000000, 0); + test__clzsi2(0xE7000000, 0); + test__clzsi2(0xE8000000, 0); + test__clzsi2(0xE9000000, 0); + test__clzsi2(0xEA000000, 0); + test__clzsi2(0xEB000000, 0); + test__clzsi2(0xEC000000, 0); + test__clzsi2(0xED000000, 0); + test__clzsi2(0xEE000000, 0); + test__clzsi2(0xEF000000, 0); + test__clzsi2(0xF0000000, 0); + test__clzsi2(0xF1000000, 0); + test__clzsi2(0xF2000000, 0); + test__clzsi2(0xF3000000, 0); + test__clzsi2(0xF4000000, 0); + test__clzsi2(0xF5000000, 0); + test__clzsi2(0xF6000000, 0); + test__clzsi2(0xF7000000, 0); + test__clzsi2(0xF8000000, 0); + test__clzsi2(0xF9000000, 0); + test__clzsi2(0xFA000000, 0); + test__clzsi2(0xFB000000, 0); + test__clzsi2(0xFC000000, 0); + test__clzsi2(0xFD000000, 0); + test__clzsi2(0xFE000000, 0); + test__clzsi2(0xFF000000, 0); + test__clzsi2(0x00000001, 31); + test__clzsi2(0x00000002, 30); + test__clzsi2(0x00000004, 29); + test__clzsi2(0x00000008, 28); + test__clzsi2(0x00000010, 27); + test__clzsi2(0x00000020, 26); + test__clzsi2(0x00000040, 25); + test__clzsi2(0x00000080, 24); + test__clzsi2(0x00000100, 23); + test__clzsi2(0x00000200, 22); + test__clzsi2(0x00000400, 21); + test__clzsi2(0x00000800, 20); + test__clzsi2(0x00001000, 19); + test__clzsi2(0x00002000, 18); + test__clzsi2(0x00004000, 17); + test__clzsi2(0x00008000, 16); + test__clzsi2(0x00010000, 15); + test__clzsi2(0x00020000, 14); + test__clzsi2(0x00040000, 13); + test__clzsi2(0x00080000, 12); + test__clzsi2(0x00100000, 11); + test__clzsi2(0x00200000, 10); + test__clzsi2(0x00400000, 9); +} diff --git a/lib/std/special/compiler_rt/compareXf2.zig b/lib/std/special/compiler_rt/compareXf2.zig new file mode 100644 index 0000000000..15e49e3cc1 --- /dev/null +++ b/lib/std/special/compiler_rt/compareXf2.zig @@ -0,0 +1,253 @@ +// 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 LE = extern enum(i32) { + Less = -1, + Equal = 0, + Greater = 1, + Unordered = 1, +}; + +const GE = extern enum(i32) { + Less = -1, + Equal = 0, + Greater = 1, + Unordered = -1, +}; + +pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT { + @setRuntimeSafety(builtin.is_test); + + const srep_t = @IntType(true, T.bit_count); + const rep_t = @IntType(false, T.bit_count); + + 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 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 .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 fp_ting-point compare. + if ((aInt & bInt) >= 0) { + if (aInt < bInt) { + return .Less; + } else if (aInt == bInt) { + return .Equal; + } else return .Greater; + } + + // 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). + else { + if (aInt > bInt) { + return .Less; + } else if (aInt == bInt) { + return .Equal; + } else return .Greater; + } +} + +pub fn unordcmp(comptime T: type, a: T, b: T) i32 { + @setRuntimeSafety(builtin.is_test); + + const rep_t = @IntType(false, T.bit_count); + + 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); + return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f32, LE, a, b })); +} + +pub fn __gesf2(a: f32, b: f32) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f32, GE, a, b })); +} + +pub fn __eqsf2(a: f32, b: f32) callconv(.C) i32 { + return __lesf2(a, b); +} + +pub fn __ltsf2(a: f32, b: f32) callconv(.C) i32 { + return __lesf2(a, b); +} + +pub fn __nesf2(a: f32, b: f32) callconv(.C) i32 { + return __lesf2(a, b); +} + +pub fn __gtsf2(a: f32, b: f32) callconv(.C) i32 { + return __gesf2(a, b); +} + +// Comparison between f64 + +pub fn __ledf2(a: f64, b: f64) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f64, LE, a, b })); +} + +pub fn __gedf2(a: f64, b: f64) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f64, GE, a, b })); +} + +pub fn __eqdf2(a: f64, b: f64) callconv(.C) i32 { + return __ledf2(a, b); +} + +pub fn __ltdf2(a: f64, b: f64) callconv(.C) i32 { + return __ledf2(a, b); +} + +pub fn __nedf2(a: f64, b: f64) callconv(.C) i32 { + return __ledf2(a, b); +} + +pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { + return __gedf2(a, b); +} + +// Comparison between f128 + +pub fn __letf2(a: f128, b: f128) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f128, LE, a, b })); +} + +pub fn __getf2(a: f128, b: f128) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f128, GE, a, b })); +} + +pub fn __eqtf2(a: f128, b: f128) callconv(.C) i32 { + return __letf2(a, b); +} + +pub fn __lttf2(a: f128, b: f128) callconv(.C) i32 { + return __letf2(a, b); +} + +pub fn __netf2(a: f128, b: f128) callconv(.C) i32 { + return __letf2(a, b); +} + +pub fn __gttf2(a: f128, b: f128) callconv(.C) i32 { + return __getf2(a, b); +} + +// Unordered comparison between f32/f64/f128 + +pub fn __unordsf2(a: f32, b: f32) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + return @call(.{ .modifier = .always_inline }, unordcmp, .{ f32, a, b }); +} + +pub fn __unorddf2(a: f64, b: f64) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + return @call(.{ .modifier = .always_inline }, unordcmp, .{ f64, a, b }); +} + +pub fn __unordtf2(a: f128, b: f128) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + return @call(.{ .modifier = .always_inline }, unordcmp, .{ f128, 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/std/special/compiler_rt/comparedf2.zig b/lib/std/special/compiler_rt/comparedf2.zig deleted file mode 100644 index 98cca106f7..0000000000 --- a/lib/std/special/compiler_rt/comparedf2.zig +++ /dev/null @@ -1,127 +0,0 @@ -// Ported from: -// -// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/comparedf2.c - -const std = @import("std"); -const builtin = @import("builtin"); -const is_test = builtin.is_test; - -const fp_t = f64; -const rep_t = u64; -const srep_t = i64; - -const typeWidth = rep_t.bit_count; -const significandBits = std.math.floatMantissaBits(fp_t); -const exponentBits = std.math.floatExponentBits(fp_t); -const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); -const absMask = signBit - 1; -const implicitBit = @as(rep_t, 1) << significandBits; -const significandMask = implicitBit - 1; -const exponentMask = absMask ^ significandMask; -const infRep = @bitCast(rep_t, std.math.inf(fp_t)); - -// TODO https://github.com/ziglang/zig/issues/641 -// and then make the return types of some of these functions the enum instead of c_int -const LE_LESS = @as(c_int, -1); -const LE_EQUAL = @as(c_int, 0); -const LE_GREATER = @as(c_int, 1); -const LE_UNORDERED = @as(c_int, 1); - -pub fn __ledf2(a: fp_t, b: fp_t) callconv(.C) c_int { - @setRuntimeSafety(is_test); - const aInt: srep_t = @bitCast(srep_t, a); - const bInt: srep_t = @bitCast(srep_t, b); - const aAbs: rep_t = @bitCast(rep_t, aInt) & absMask; - const bAbs: rep_t = @bitCast(rep_t, bInt) & absMask; - - // If either a or b is NaN, they are unordered. - if (aAbs > infRep or bAbs > infRep) return LE_UNORDERED; - - // If a and b are both zeros, they are equal. - if ((aAbs | bAbs) == 0) return LE_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 fp_ting-point compare. - if ((aInt & bInt) >= 0) { - if (aInt < bInt) { - return LE_LESS; - } else if (aInt == bInt) { - return LE_EQUAL; - } else return LE_GREATER; - } - - // 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). - else { - if (aInt > bInt) { - return LE_LESS; - } else if (aInt == bInt) { - return LE_EQUAL; - } else return LE_GREATER; - } -} - -// TODO https://github.com/ziglang/zig/issues/641 -// and then make the return types of some of these functions the enum instead of c_int -const GE_LESS = @as(c_int, -1); -const GE_EQUAL = @as(c_int, 0); -const GE_GREATER = @as(c_int, 1); -const GE_UNORDERED = @as(c_int, -1); // Note: different from LE_UNORDERED - -pub fn __gedf2(a: fp_t, b: fp_t) callconv(.C) c_int { - @setRuntimeSafety(is_test); - const aInt: srep_t = @bitCast(srep_t, a); - const bInt: srep_t = @bitCast(srep_t, b); - const aAbs: rep_t = @bitCast(rep_t, aInt) & absMask; - const bAbs: rep_t = @bitCast(rep_t, bInt) & absMask; - - if (aAbs > infRep or bAbs > infRep) return GE_UNORDERED; - if ((aAbs | bAbs) == 0) return GE_EQUAL; - if ((aInt & bInt) >= 0) { - if (aInt < bInt) { - return GE_LESS; - } else if (aInt == bInt) { - return GE_EQUAL; - } else return GE_GREATER; - } else { - if (aInt > bInt) { - return GE_LESS; - } else if (aInt == bInt) { - return GE_EQUAL; - } else return GE_GREATER; - } -} - -pub fn __unorddf2(a: fp_t, b: fp_t) callconv(.C) c_int { - @setRuntimeSafety(is_test); - 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); -} - -pub fn __eqdf2(a: fp_t, b: fp_t) callconv(.C) c_int { - return __ledf2(a, b); -} - -pub fn __ltdf2(a: fp_t, b: fp_t) callconv(.C) c_int { - return __ledf2(a, b); -} - -pub fn __nedf2(a: fp_t, b: fp_t) callconv(.C) c_int { - return __ledf2(a, b); -} - -pub fn __gtdf2(a: fp_t, b: fp_t) callconv(.C) c_int { - return __gedf2(a, b); -} - -pub fn __aeabi_dcmpun(a: fp_t, b: fp_t) callconv(.AAPCS) c_int { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __unorddf2, .{ a, b }); -} - -test "import comparedf2" { - _ = @import("comparedf2_test.zig"); -} diff --git a/lib/std/special/compiler_rt/comparedf2_test.zig b/lib/std/special/compiler_rt/comparedf2_test.zig index b0e5757ec0..16a2a258ce 100644 --- a/lib/std/special/compiler_rt/comparedf2_test.zig +++ b/lib/std/special/compiler_rt/comparedf2_test.zig @@ -6,7 +6,7 @@ const std = @import("std"); const builtin = @import("builtin"); const is_test = builtin.is_test; -const comparedf2 = @import("comparedf2.zig"); +const comparedf2 = @import("compareXf2.zig"); const TestVector = struct { a: f64, diff --git a/lib/std/special/compiler_rt/comparesf2.zig b/lib/std/special/compiler_rt/comparesf2.zig deleted file mode 100644 index bd881af2a1..0000000000 --- a/lib/std/special/compiler_rt/comparesf2.zig +++ /dev/null @@ -1,127 +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 fp_t = f32; -const rep_t = u32; -const srep_t = i32; - -const typeWidth = rep_t.bit_count; -const significandBits = std.math.floatMantissaBits(fp_t); -const exponentBits = std.math.floatExponentBits(fp_t); -const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); -const absMask = signBit - 1; -const implicitBit = @as(rep_t, 1) << significandBits; -const significandMask = implicitBit - 1; -const exponentMask = absMask ^ significandMask; -const infRep = @bitCast(rep_t, std.math.inf(fp_t)); - -// TODO https://github.com/ziglang/zig/issues/641 -// and then make the return types of some of these functions the enum instead of c_int -const LE_LESS = @as(c_int, -1); -const LE_EQUAL = @as(c_int, 0); -const LE_GREATER = @as(c_int, 1); -const LE_UNORDERED = @as(c_int, 1); - -pub fn __lesf2(a: fp_t, b: fp_t) callconv(.C) c_int { - @setRuntimeSafety(is_test); - const aInt: srep_t = @bitCast(srep_t, a); - const bInt: srep_t = @bitCast(srep_t, b); - const aAbs: rep_t = @bitCast(rep_t, aInt) & absMask; - const bAbs: rep_t = @bitCast(rep_t, bInt) & absMask; - - // If either a or b is NaN, they are unordered. - if (aAbs > infRep or bAbs > infRep) return LE_UNORDERED; - - // If a and b are both zeros, they are equal. - if ((aAbs | bAbs) == 0) return LE_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 fp_ting-point compare. - if ((aInt & bInt) >= 0) { - if (aInt < bInt) { - return LE_LESS; - } else if (aInt == bInt) { - return LE_EQUAL; - } else return LE_GREATER; - } - - // 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). - else { - if (aInt > bInt) { - return LE_LESS; - } else if (aInt == bInt) { - return LE_EQUAL; - } else return LE_GREATER; - } -} - -// TODO https://github.com/ziglang/zig/issues/641 -// and then make the return types of some of these functions the enum instead of c_int -const GE_LESS = @as(c_int, -1); -const GE_EQUAL = @as(c_int, 0); -const GE_GREATER = @as(c_int, 1); -const GE_UNORDERED = @as(c_int, -1); // Note: different from LE_UNORDERED - -pub fn __gesf2(a: fp_t, b: fp_t) callconv(.C) c_int { - @setRuntimeSafety(is_test); - const aInt: srep_t = @bitCast(srep_t, a); - const bInt: srep_t = @bitCast(srep_t, b); - const aAbs: rep_t = @bitCast(rep_t, aInt) & absMask; - const bAbs: rep_t = @bitCast(rep_t, bInt) & absMask; - - if (aAbs > infRep or bAbs > infRep) return GE_UNORDERED; - if ((aAbs | bAbs) == 0) return GE_EQUAL; - if ((aInt & bInt) >= 0) { - if (aInt < bInt) { - return GE_LESS; - } else if (aInt == bInt) { - return GE_EQUAL; - } else return GE_GREATER; - } else { - if (aInt > bInt) { - return GE_LESS; - } else if (aInt == bInt) { - return GE_EQUAL; - } else return GE_GREATER; - } -} - -pub fn __unordsf2(a: fp_t, b: fp_t) callconv(.C) c_int { - @setRuntimeSafety(is_test); - 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); -} - -pub fn __eqsf2(a: fp_t, b: fp_t) callconv(.C) c_int { - return __lesf2(a, b); -} - -pub fn __ltsf2(a: fp_t, b: fp_t) callconv(.C) c_int { - return __lesf2(a, b); -} - -pub fn __nesf2(a: fp_t, b: fp_t) callconv(.C) c_int { - return __lesf2(a, b); -} - -pub fn __gtsf2(a: fp_t, b: fp_t) callconv(.C) c_int { - return __gesf2(a, b); -} - -pub fn __aeabi_fcmpun(a: fp_t, b: fp_t) callconv(.AAPCS) c_int { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __unordsf2, .{ a, b }); -} - -test "import comparesf2" { - _ = @import("comparesf2_test.zig"); -} diff --git a/lib/std/special/compiler_rt/comparesf2_test.zig b/lib/std/special/compiler_rt/comparesf2_test.zig index d736988bfb..e3966c021b 100644 --- a/lib/std/special/compiler_rt/comparesf2_test.zig +++ b/lib/std/special/compiler_rt/comparesf2_test.zig @@ -6,7 +6,7 @@ const std = @import("std"); const builtin = @import("builtin"); const is_test = builtin.is_test; -const comparesf2 = @import("comparesf2.zig"); +const comparesf2 = @import("compareXf2.zig"); const TestVector = struct { a: f32, diff --git a/lib/std/special/compiler_rt/comparetf2.zig b/lib/std/special/compiler_rt/comparetf2.zig deleted file mode 100644 index f2969f2112..0000000000 --- a/lib/std/special/compiler_rt/comparetf2.zig +++ /dev/null @@ -1,99 +0,0 @@ -// TODO https://github.com/ziglang/zig/issues/641 -// and then make the return types of some of these functions the enum instead of c_int -const LE_LESS = @as(c_int, -1); -const LE_EQUAL = @as(c_int, 0); -const LE_GREATER = @as(c_int, 1); -const LE_UNORDERED = @as(c_int, 1); - -const rep_t = u128; -const srep_t = i128; - -const typeWidth = rep_t.bit_count; -const significandBits = 112; -const exponentBits = (typeWidth - significandBits - 1); -const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); -const absMask = signBit - 1; -const implicitBit = @as(rep_t, 1) << significandBits; -const significandMask = implicitBit - 1; -const exponentMask = absMask ^ significandMask; -const infRep = exponentMask; - -const builtin = @import("builtin"); -const is_test = builtin.is_test; - -pub fn __letf2(a: f128, b: f128) callconv(.C) c_int { - @setRuntimeSafety(is_test); - - const aInt = @bitCast(rep_t, a); - const bInt = @bitCast(rep_t, b); - - const aAbs: rep_t = aInt & absMask; - const bAbs: rep_t = bInt & absMask; - - // If either a or b is NaN, they are unordered. - if (aAbs > infRep or bAbs > infRep) return LE_UNORDERED; - - // If a and b are both zeros, they are equal. - if ((aAbs | bAbs) == 0) return LE_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. - return if ((aInt & bInt) >= 0) - if (aInt < bInt) - LE_LESS - else if (aInt == bInt) - LE_EQUAL - else - LE_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) - LE_LESS - else if (aInt == bInt) - LE_EQUAL - else - LE_GREATER; -} - -// TODO https://github.com/ziglang/zig/issues/641 -// and then make the return types of some of these functions the enum instead of c_int -const GE_LESS = @as(c_int, -1); -const GE_EQUAL = @as(c_int, 0); -const GE_GREATER = @as(c_int, 1); -const GE_UNORDERED = @as(c_int, -1); // Note: different from LE_UNORDERED - -pub fn __getf2(a: f128, b: f128) callconv(.C) c_int { - @setRuntimeSafety(is_test); - - 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 (aAbs > infRep or bAbs > infRep) return GE_UNORDERED; - if ((aAbs | bAbs) == 0) return GE_EQUAL; - return if ((aInt & bInt) >= 0) - if (aInt < bInt) - GE_LESS - else if (aInt == bInt) - GE_EQUAL - else - GE_GREATER - else if (aInt > bInt) - GE_LESS - else if (aInt == bInt) - GE_EQUAL - else - GE_GREATER; -} - -pub fn __unordtf2(a: f128, b: f128) callconv(.C) c_int { - @setRuntimeSafety(is_test); - - const aAbs = @bitCast(rep_t, a) & absMask; - const bAbs = @bitCast(rep_t, b) & absMask; - return @boolToInt(aAbs > infRep or bAbs > infRep); -} diff --git a/lib/std/start.zig b/lib/std/start.zig index c3844e4d1e..bf6f61f25f 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -8,16 +8,7 @@ const uefi = std.os.uefi; var starting_stack_ptr: [*]usize = undefined; -const is_wasm = switch (builtin.arch) { - .wasm32, .wasm64 => true, - else => false, -}; - -const is_mips = switch (builtin.arch) { - .mips, .mipsel, .mips64, .mips64el => true, - else => false, -}; -const start_sym_name = if (is_mips) "__start" else "_start"; +const start_sym_name = if (builtin.arch.isMIPS()) "__start" else "_start"; comptime { if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) { @@ -35,7 +26,7 @@ comptime { } } else if (builtin.os == .uefi) { if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" }); - } else if (is_wasm and builtin.os == .freestanding) { + } else if (builtin.arch.isWasm() and builtin.os == .freestanding) { if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name }); } else if (builtin.os != .other and builtin.os != .freestanding) { if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name }); diff --git a/lib/std/target.zig b/lib/std/target.zig index 22fea691c4..d62786ff7f 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -125,6 +125,16 @@ pub const Target = union(enum) { v5, v5te, v4t, + + pub fn version(version: Arm32) comptime_int { + return switch (version) { + .v8_5a, .v8_4a, .v8_3a, .v8_2a, .v8_1a, .v8, .v8r, .v8m_baseline, .v8m_mainline, .v8_1m_mainline => 8, + .v7, .v7em, .v7m, .v7s, .v7k, .v7ve => 7, + .v6, .v6m, .v6k, .v6t2 => 6, + .v5, .v5te => 5, + .v4t => 4, + }; + } }; pub const Arm64 = enum { v8_5a, @@ -146,6 +156,34 @@ pub const Target = union(enum) { r6, }; + pub fn isARM(arch: Arch) bool { + return switch (arch) { + .arm, .armeb => true, + else => false, + }; + } + + pub fn isThumb(arch: Arch) bool { + return switch (arch) { + .thumb, .thumbeb => true, + else => false, + }; + } + + pub fn isWasm(arch: Arch) bool { + return switch (arch) { + .wasm32, .wasm64 => true, + else => false, + }; + } + + pub fn isMIPS(arch: Arch) bool { + return switch (arch) { + .mips, .mipsel, .mips64, .mips64el => true, + else => false, + }; + } + pub fn toElfMachine(arch: Arch) std.elf.EM { return switch (arch) { .avr => ._AVR, diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index b45d0f61f0..bade253a07 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -289,8 +289,7 @@ pub fn translate( tree.errors = ast.Tree.ErrorList.init(arena); tree.root_node = try arena.create(ast.Node.Root); - tree.root_node.* = ast.Node.Root{ - .base = ast.Node{ .id = ast.Node.Id.Root }, + tree.root_node.* = .{ .decls = ast.Node.Root.DeclList.init(arena), // initialized with the eof token at the end .eof_token = undefined, @@ -440,7 +439,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}), .Auto => unreachable, // Not legal on functions .Register => unreachable, // Not legal on functions - else => unreachable, }, }; @@ -877,25 +875,23 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No // types, while that's not ISO-C compliant many compilers allow this and // default to the usual integer type used for all the enums. - // TODO only emit this tag type if the enum tag type is not the default. - // I don't know what the default is, need to figure out how clang is deciding. - // it appears to at least be different across gcc/msvc - if (int_type.ptr != null and - !isCBuiltinType(int_type, .UInt) and - !isCBuiltinType(int_type, .Int)) - { - _ = try appendToken(c, .LParen, "("); - container_node.init_arg_expr = .{ - .Type = transQualType(rp, int_type, enum_loc) catch |err| switch (err) { + // default to c_int since msvc and gcc default to different types + _ = try appendToken(c, .LParen, "("); + container_node.init_arg_expr = .{ + .Type = if (int_type.ptr != null and + !isCBuiltinType(int_type, .UInt) and + !isCBuiltinType(int_type, .Int)) + transQualType(rp, int_type, enum_loc) catch |err| switch (err) { error.UnsupportedType => { try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{}); return null; }, else => |e| return e, - }, - }; - _ = try appendToken(c, .RParen, ")"); - } + } + else + try transCreateNodeIdentifier(c, "c_int"), + }; + _ = try appendToken(c, .RParen, ")"); container_node.lbrace_token = try appendToken(c, .LBrace, "{"); @@ -953,6 +949,19 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No tld_node.semicolon_token = try appendToken(c, .Semicolon, ";"); try addTopLevelDecl(c, field_name, &tld_node.base); } + // make non exhaustive + const field_node = try c.a().create(ast.Node.ContainerField); + field_node.* = .{ + .doc_comments = null, + .comptime_token = null, + .name_token = try appendIdentifier(c, "_"), + .type_expr = null, + .value_expr = null, + .align_expr = null, + }; + + try container_node.fields_and_decls.push(&field_node.base); + _ = try appendToken(c, .Comma, ","); container_node.rbrace_token = try appendToken(c, .RBrace, "}"); break :blk &container_node.base; @@ -1231,18 +1240,6 @@ fn transBinaryOperator( op_id = .BitOr; op_token = try appendToken(rp.c, .Pipe, "|"); }, - .Assign, - .MulAssign, - .DivAssign, - .RemAssign, - .AddAssign, - .SubAssign, - .ShlAssign, - .ShrAssign, - .AndAssign, - .XorAssign, - .OrAssign, - => unreachable, else => unreachable, } @@ -1678,7 +1675,6 @@ fn transStringLiteral( "TODO: support string literal kind {}", .{kind}, ), - else => unreachable, } } @@ -2206,6 +2202,19 @@ fn transDoWhileLoop( .id = .Loop, }; + // if (!cond) break; + const if_node = try transCreateNodeIf(rp.c); + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, + }; + const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); + prefix_op.rhs = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true); + _ = try appendToken(rp.c, .RParen, ")"); + if_node.condition = &prefix_op.base; + if_node.body = &(try transCreateNodeBreak(rp.c, null)).base; + _ = try appendToken(rp.c, .Semicolon, ";"); + const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: { // there's already a block in C, so we'll append our condition to it. // c: do { @@ -2217,10 +2226,7 @@ fn transDoWhileLoop( // zig: b; // zig: if (!cond) break; // zig: } - const body = (try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?; - // if this is used as an expression in Zig it needs to be immediately followed by a semicolon - _ = try appendToken(rp.c, .Semicolon, ";"); - break :blk body; + break :blk (try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?; } else blk: { // the C statement is without a block, so we need to create a block to contain it. // c: do @@ -2236,19 +2242,6 @@ fn transDoWhileLoop( break :blk block; }; - // if (!cond) break; - const if_node = try transCreateNodeIf(rp.c); - var cond_scope = Scope{ - .parent = scope, - .id = .Condition, - }; - const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); - prefix_op.rhs = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true); - _ = try appendToken(rp.c, .RParen, ")"); - if_node.condition = &prefix_op.base; - if_node.body = &(try transCreateNodeBreak(rp.c, null)).base; - _ = try appendToken(rp.c, .Semicolon, ";"); - try body_node.statements.push(&if_node.base); if (new) body_node.rbrace = try appendToken(rp.c, .RBrace, "}"); @@ -4783,8 +4776,7 @@ fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex { fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { const token_index = try appendIdentifier(c, name); const identifier = try c.a().create(ast.Node.Identifier); - identifier.* = ast.Node.Identifier{ - .base = ast.Node{ .id = ast.Node.Id.Identifier }, + identifier.* = .{ .token = token_index, }; return &identifier.base; @@ -4923,8 +4915,7 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u const token_index = try appendToken(c, .Keyword_var, "var"); const identifier = try c.a().create(ast.Node.Identifier); - identifier.* = ast.Node.Identifier{ - .base = ast.Node{ .id = ast.Node.Id.Identifier }, + identifier.* = .{ .token = token_index, }; diff --git a/src/all_types.hpp b/src/all_types.hpp index 0fed73f7b2..df52c29a4e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -358,6 +358,8 @@ struct LazyValueSizeOf { IrAnalyze *ira; IrInstruction *target_type; + + bool bit_size; }; struct LazyValueSliceType { @@ -1383,6 +1385,7 @@ struct ZigTypeEnum { ContainerLayout layout; ResolveStatus resolve_status; + bool non_exhaustive; bool resolve_loop_flag; }; @@ -1754,6 +1757,7 @@ enum BuiltinFnId { BuiltinFnIdFrameSize, BuiltinFnIdAs, BuiltinFnIdCall, + BuiltinFnIdBitSizeof, }; struct BuiltinFnEntry { @@ -3146,6 +3150,7 @@ struct IrInstructionAsmGen { struct IrInstructionSizeOf { IrInstruction base; + bool bit_size; IrInstruction *type_value; }; @@ -3665,6 +3670,7 @@ struct IrInstructionCheckSwitchProngs { IrInstructionCheckSwitchProngsRange *ranges; size_t range_count; bool have_else_prong; + bool have_underscore_prong; }; struct IrInstructionCheckStatementIsVoid { diff --git a/src/analyze.cpp b/src/analyze.cpp index b7838003c8..638b0b03b0 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2569,15 +2569,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { return ErrorSemanticAnalyzeFail; } - enum_type->data.enumeration.src_field_count = field_count; - enum_type->data.enumeration.fields = allocate(field_count); - enum_type->data.enumeration.fields_by_name.init(field_count); - Scope *scope = &enum_type->data.enumeration.decls_scope->base; - HashMap occupied_tag_values = {}; - occupied_tag_values.init(field_count); - ZigType *tag_int_type; if (enum_type->data.enumeration.layout == ContainerLayoutExtern) { tag_int_type = get_c_int_type(g, CIntTypeInt); @@ -2612,13 +2605,14 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { buf_ptr(&wanted_tag_int_type->name))); add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr, buf_sprintf("any integral type of size 8, 16, 32, 64 or 128 bit is valid")); - return ErrorNone; + return ErrorSemanticAnalyzeFail; } } tag_int_type = wanted_tag_int_type; } } + enum_type->data.enumeration.non_exhaustive = false; enum_type->data.enumeration.tag_int_type = tag_int_type; enum_type->size_in_bits = tag_int_type->size_in_bits; enum_type->abi_size = tag_int_type->abi_size; @@ -2627,6 +2621,31 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { BigInt bi_one; bigint_init_unsigned(&bi_one, 1); + AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1); + if (buf_eql_str(last_field_node->data.struct_field.name, "_")) { + field_count -= 1; + if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) { + add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum specifies every value")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } + if (decl_node->data.container_decl.init_arg_expr == nullptr) { + add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum must specify size")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } + if (last_field_node->data.struct_field.value != nullptr) { + add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } + enum_type->data.enumeration.non_exhaustive = true; + } + + enum_type->data.enumeration.src_field_count = field_count; + enum_type->data.enumeration.fields = allocate(field_count); + enum_type->data.enumeration.fields_by_name.init(field_count); + + HashMap occupied_tag_values = {}; + occupied_tag_values.init(field_count); + TypeEnumField *last_enum_field = nullptr; for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { @@ -2648,6 +2667,11 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { buf_sprintf("consider 'union(enum)' here")); } + if (buf_eql_str(type_enum_field->name, "_")) { + add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } + auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field); if (field_entry != nullptr) { ErrorMsg *msg = add_node_error(g, field_node, @@ -8288,7 +8312,7 @@ static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatu uint32_t field_count = enum_type->data.enumeration.src_field_count; - assert(enum_type->data.enumeration.fields); + assert(field_count == 0 || enum_type->data.enumeration.fields != nullptr); ZigLLVMDIEnumerator **di_enumerators = allocate(field_count); for (uint32_t i = 0; i < field_count; i += 1) { diff --git a/src/codegen.cpp b/src/codegen.cpp index cb7bbaec45..a5d4c6d500 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3356,7 +3356,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), instruction->target->value->type, tag_int_type, target_val); - if (ir_want_runtime_safety(g, &instruction->base) && wanted_type->data.enumeration.layout != ContainerLayoutExtern) { + if (ir_want_runtime_safety(g, &instruction->base) && !wanted_type->data.enumeration.non_exhaustive) { LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue"); LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); size_t field_count = wanted_type->data.enumeration.src_field_count; @@ -5065,6 +5065,11 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable { ZigType *enum_type = instruction->target->value->type; assert(enum_type->id == ZigTypeIdEnum); + if (enum_type->data.enumeration.non_exhaustive) { + add_node_error(g, instruction->base.source_node, + buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991")); + codegen_report_errors_and_exit(g); + } LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type); @@ -8299,6 +8304,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdFrameSize, "frameSize", 1); create_builtin_fn(g, BuiltinFnIdAs, "as", 2); create_builtin_fn(g, BuiltinFnIdCall, "call", 3); + create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index d871aa27a0..db1faac37c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2392,9 +2392,10 @@ static IrInstruction *ir_build_asm_gen(IrAnalyze *ira, Scope *scope, AstNode *so return &instruction->base; } -static IrInstruction *ir_build_size_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { +static IrInstruction *ir_build_size_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, bool bit_size) { IrInstructionSizeOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; + instruction->bit_size = bit_size; ir_ref_instruction(type_value, irb->current_basic_block); @@ -3451,7 +3452,7 @@ static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count, - bool have_else_prong) + bool have_else_prong, bool have_underscore_prong) { IrInstructionCheckSwitchProngs *instruction = ir_build_instruction( irb, scope, source_node); @@ -3459,6 +3460,7 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, instruction->ranges = ranges; instruction->range_count = range_count; instruction->have_else_prong = have_else_prong; + instruction->have_underscore_prong = have_underscore_prong; ir_ref_instruction(target_value, irb->current_basic_block); for (size_t i = 0; i < range_count; i += 1) { @@ -5249,13 +5251,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_lval_wrap(irb, scope, set_float_mode, lval, result_loc); } case BuiltinFnIdSizeof: + case BuiltinFnIdBitSizeof: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value); + IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value, builtin_fn->id == BuiltinFnIdBitSizeof); return ir_lval_wrap(irb, scope, size_of, lval, result_loc); } case BuiltinFnIdImport: @@ -6027,8 +6030,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value); - IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, actual_tag); + IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, tag_name, lval, result_loc); } case BuiltinFnIdTagType: @@ -8090,34 +8092,11 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); AstNode *else_prong = nullptr; + AstNode *underscore_prong = nullptr; for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); size_t prong_item_count = prong_node->data.switch_prong.items.length; - if (prong_item_count == 0) { - ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); - if (else_prong) { - ErrorMsg *msg = add_node_error(irb->codegen, prong_node, - buf_sprintf("multiple else prongs in switch expression")); - add_error_note(irb->codegen, msg, else_prong, - buf_sprintf("previous else prong is here")); - return irb->codegen->invalid_instruction; - } - else_prong = prong_node; - - IrBasicBlock *prev_block = irb->current_basic_block; - if (peer_parent->peers.length > 0) { - peer_parent->peers.last()->next_bb = else_block; - } - peer_parent->peers.append(this_peer_result_loc); - ir_set_cursor_at_end_and_append_block(irb, else_block); - if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, - is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values, - &switch_else_var, LValNone, &this_peer_result_loc->base)) - { - return irb->codegen->invalid_instruction; - } - ir_set_cursor_at_end(irb, prev_block); - } else if (prong_node->data.switch_prong.any_items_are_range) { + if (prong_node->data.switch_prong.any_items_are_range) { ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); IrInstruction *ok_bit = nullptr; @@ -8195,6 +8174,56 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * } ir_set_cursor_at_end_and_append_block(irb, range_block_no); + } else { + if (prong_item_count == 0) { + if (else_prong) { + ErrorMsg *msg = add_node_error(irb->codegen, prong_node, + buf_sprintf("multiple else prongs in switch expression")); + add_error_note(irb->codegen, msg, else_prong, + buf_sprintf("previous else prong is here")); + return irb->codegen->invalid_instruction; + } + else_prong = prong_node; + } else if (prong_item_count == 1 && + prong_node->data.switch_prong.items.at(0)->type == NodeTypeSymbol && + buf_eql_str(prong_node->data.switch_prong.items.at(0)->data.symbol_expr.symbol, "_")) { + if (underscore_prong) { + ErrorMsg *msg = add_node_error(irb->codegen, prong_node, + buf_sprintf("multiple '_' prongs in switch expression")); + add_error_note(irb->codegen, msg, underscore_prong, + buf_sprintf("previous '_' prong is here")); + return irb->codegen->invalid_instruction; + } + underscore_prong = prong_node; + } else { + continue; + } + if (underscore_prong && else_prong) { + ErrorMsg *msg = add_node_error(irb->codegen, prong_node, + buf_sprintf("else and '_' prong in switch expression")); + if (underscore_prong == prong_node) + add_error_note(irb->codegen, msg, else_prong, + buf_sprintf("else prong is here")); + else + add_error_note(irb->codegen, msg, underscore_prong, + buf_sprintf("'_' prong is here")); + return irb->codegen->invalid_instruction; + } + ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); + + IrBasicBlock *prev_block = irb->current_basic_block; + if (peer_parent->peers.length > 0) { + peer_parent->peers.last()->next_bb = else_block; + } + peer_parent->peers.append(this_peer_result_loc); + ir_set_cursor_at_end_and_append_block(irb, else_block); + if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, + is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values, + &switch_else_var, LValNone, &this_peer_result_loc->base)) + { + return irb->codegen->invalid_instruction; + } + ir_set_cursor_at_end(irb, prev_block); } } @@ -8206,6 +8235,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * continue; if (prong_node->data.switch_prong.any_items_are_range) continue; + if (underscore_prong == prong_node) + continue; ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); @@ -8249,7 +8280,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * } IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, - check_ranges.items, check_ranges.length, else_prong != nullptr); + check_ranges.items, check_ranges.length, else_prong != nullptr, underscore_prong != nullptr); IrInstruction *br_instruction; if (cases.length == 0) { @@ -8269,7 +8300,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction; } - if (!else_prong) { + if (!else_prong && !underscore_prong) { if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = else_block; } @@ -12790,7 +12821,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour return ira->codegen->invalid_instruction; TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); - if (field == nullptr && wanted_type->data.enumeration.layout != ContainerLayoutExtern) { + if (field == nullptr && !wanted_type->data.enumeration.non_exhaustive) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &val->data.x_bigint, 10); ErrorMsg *msg = ir_add_error(ira, source_instr, @@ -21065,6 +21096,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructi lazy_size_of->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_size_of->base; lazy_size_of->base.id = LazyValueIdSizeOf; + lazy_size_of->bit_size = instruction->bit_size; lazy_size_of->target_type = instruction->type_value->child; if (ir_resolve_type_lazy(ira, lazy_size_of->target_type) == nullptr) @@ -21356,10 +21388,6 @@ static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; - if (value->value->type->id == ZigTypeIdEnum) { - return value; - } - if (value->value->type->id != ZigTypeIdUnion) { ir_add_error(ira, value, buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value->type->name))); @@ -21427,12 +21455,6 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, if (type_is_invalid(case_value->value->type)) return ir_unreach_error(ira); - if (case_value->value->type->id == ZigTypeIdEnum) { - case_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, case_value); - if (type_is_invalid(case_value->value->type)) - return ir_unreach_error(ira); - } - IrInstruction *casted_case_value = ir_implicit_cast(ira, case_value, target_value->value->type); if (type_is_invalid(casted_case_value->value->type)) return ir_unreach_error(ira); @@ -21477,12 +21499,6 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, if (type_is_invalid(new_value->value->type)) continue; - if (new_value->value->type->id == ZigTypeIdEnum) { - new_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, new_value); - if (type_is_invalid(new_value->value->type)) - continue; - } - IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, target_value->value->type); if (type_is_invalid(casted_new_value->value->type)) continue; @@ -21598,7 +21614,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, case ZigTypeIdEnum: { if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; - if (target_type->data.enumeration.src_field_count < 2) { + if (target_type->data.enumeration.src_field_count == 1) { TypeEnumField *only_field = &target_type->data.enumeration.fields[0]; IrInstruction *result = ir_const(ira, &switch_target_instruction->base, target_type); bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value); @@ -22319,11 +22335,39 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; + if (target->value->type->id == ZigTypeIdEnumLiteral) { + IrInstruction *result = ir_const(ira, &instruction->base, nullptr); + Buf *field_name = target->value->data.x_enum_literal; + ZigValue *array_val = create_const_str_lit(ira->codegen, field_name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field_name), true); + return result; + } + + if (target->value->type->id == ZigTypeIdUnion) { + target = ir_analyze_union_tag(ira, &instruction->base, target); + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_instruction; + } + assert(target->value->type->id == ZigTypeIdEnum); + if (target->value->type->data.enumeration.src_field_count == 1 && + !target->value->type->data.enumeration.non_exhaustive) { + TypeEnumField *only_field = &target->value->type->data.enumeration.fields[0]; + ZigValue *array_val = create_const_str_lit(ira->codegen, only_field->name)->data.x_ptr.data.ref.pointee; + IrInstruction *result = ir_const(ira, &instruction->base, nullptr); + init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(only_field->name), true); + return result; + } + if (instr_is_comptime(target)) { if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; + if (target->value->type->data.enumeration.non_exhaustive) { + add_node_error(ira->codegen, instruction->base.source_node, + buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991")); + return ira->codegen->invalid_instruction; + } TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee; IrInstruction *result = ir_const(ira, &instruction->base, nullptr); @@ -23074,7 +23118,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Enum", nullptr); - ZigValue **fields = alloc_const_vals_ptrs(4); + ZigValue **fields = alloc_const_vals_ptrs(5); result->data.x_struct.fields = fields; // layout: ContainerLayout @@ -23120,6 +23164,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr { return err; } + // is_exhaustive: bool + ensure_field_index(result->type, "is_exhaustive", 4); + fields[4]->special = ConstValSpecialStatic; + fields[4]->type = ira->codegen->builtin_types.entry_bool; + fields[4]->data.x_bool = !type_entry->data.enumeration.non_exhaustive; break; } @@ -23335,7 +23384,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr struct_field_val->special = ConstValSpecialStatic; struct_field_val->type = type_info_struct_field_type; - ZigValue **inner_fields = alloc_const_vals_ptrs(3); + ZigValue **inner_fields = alloc_const_vals_ptrs(4); inner_fields[1]->special = ConstValSpecialStatic; inner_fields[1]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int); @@ -23358,6 +23407,12 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr inner_fields[2]->type = ira->codegen->builtin_types.entry_type; inner_fields[2]->data.x_type = struct_field->type_entry; + // default_value: var + inner_fields[3]->special = ConstValSpecialStatic; + inner_fields[3]->type = get_optional_type(ira->codegen, struct_field->type_entry); + memoize_field_init_val(ira->codegen, type_entry, struct_field); + set_optional_payload(inner_fields[3], struct_field->init_val); + ZigValue *name = create_const_str_lit(ira->codegen, struct_field->name)->data.x_ptr.data.ref.pointee; init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(struct_field->name), true); @@ -26433,10 +26488,27 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, bigint_incr(&field_index); } } - if (!instruction->have_else_prong) { - if (switch_type->data.enumeration.layout == ContainerLayoutExtern) { + if (instruction->have_underscore_prong) { + if (!switch_type->data.enumeration.non_exhaustive){ ir_add_error(ira, &instruction->base, - buf_sprintf("switch on an extern enum must have an else prong")); + buf_sprintf("switch on non-exhaustive enum has `_` prong")); + } + for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; + if (buf_eql_str(enum_field->name, "_")) + continue; + + auto entry = field_prev_uses.maybe_get(enum_field->value); + if (!entry) { + ir_add_error(ira, &instruction->base, + buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name), + buf_ptr(enum_field->name))); + } + } + } else if (!instruction->have_else_prong) { + if (switch_type->data.enumeration.non_exhaustive) { + ir_add_error(ira, &instruction->base, + buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong")); } for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; @@ -29415,7 +29487,10 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); - bigint_init_unsigned(&val->data.x_bigint, abi_size); + if (lazy_size_of->bit_size) + bigint_init_unsigned(&val->data.x_bigint, size_in_bits); + else + bigint_init_unsigned(&val->data.x_bigint, abi_size); // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 90ab410e4d..24e030f501 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1039,7 +1039,10 @@ static void ir_print_asm_gen(IrPrint *irp, IrInstructionAsmGen *instruction) { } static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) { - fprintf(irp->f, "@sizeOf("); + if (instruction->bit_size) + fprintf(irp->f, "@bitSizeOf("); + else + fprintf(irp->f, "@sizeOf("); ir_print_other_instruction(irp, instruction->type_value); fprintf(irp->f, ")"); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 3ce5bd8801..be2a40d74d 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,56 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest("non-exhaustive enums", + \\const A = enum { + \\ a, + \\ b, + \\ _ = 1, + \\}; + \\const B = enum(u1) { + \\ a, + \\ _, + \\ b, + \\}; + \\const C = enum(u1) { + \\ a, + \\ b, + \\ _, + \\}; + \\pub export fn entry() void { + \\ _ = A; + \\ _ = B; + \\ _ = C; + \\} + , &[_][]const u8{ + "tmp.zig:4:5: error: non-exhaustive enum must specify size", + "error: value assigned to '_' field of non-exhaustive enum", + "error: non-exhaustive enum specifies every value", + "error: '_' field of non-exhaustive enum must be last", + }); + + cases.addTest("switching with non-exhaustive enums", + \\const E = enum(u8) { + \\ a, + \\ b, + \\ _, + \\}; + \\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 => {}, + \\ } + \\} + , &[_][]const u8{ + "tmp.zig:8:5: error: enumeration value 'E.b' not handled in switch", + "tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong", + }); + cases.addTest("@export with empty name string", \\pub export fn entry() void { } \\comptime { @@ -139,25 +189,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address", }); - cases.add("switch on extern enum missing else prong", - \\const i = extern enum { - \\ n = 0, - \\ o = 2, - \\ p = 4, - \\ q = 4, - \\}; - \\pub fn main() void { - \\ var x = @intToEnum(i, 52); - \\ switch (x) { - \\ .n, - \\ .o, - \\ .p => unreachable, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:9:5: error: switch on an extern enum must have an else prong", - }); - cases.add("invalid float literal", \\const std = @import("std"); \\ diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index ae2530af61..fe55b445a8 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -618,7 +618,6 @@ test "peer resolution of string literals" { .b => "two", .c => "three", .d => "four", - else => unreachable, }; expect(mem.eql(u8, cmd, "two")); } diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index d5bb2ddbb2..6187cee431 100644 --- a/test/stage1/behavior/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -11,16 +11,9 @@ test "extern enum" { }; fn doTheTest(y: c_int) void { var x = i.o; - expect(@enumToInt(x) == 2); - x = @intToEnum(i, 12); - expect(@enumToInt(x) == 12); - x = @intToEnum(i, y); - expect(@enumToInt(x) == 52); switch (x) { - .n, - .o, - .p => unreachable, - else => {}, + .n, .p => unreachable, + .o => {}, } } }; @@ -28,6 +21,70 @@ test "extern enum" { comptime S.doTheTest(52); } +test "non-exhaustive enum" { + const S = struct { + const E = enum(u8) { + a, + b, + _, + }; + fn doTheTest(y: u8) void { + var e: E = .b; + expect(switch (e) { + .a => false, + .b => true, + _ => false, + }); + e = @intToEnum(E, 12); + expect(switch (e) { + .a => false, + .b => false, + _ => true, + }); + + expect(switch (e) { + .a => false, + .b => false, + else => true, + }); + e = .b; + expect(switch (e) { + .a => false, + else => true, + }); + + expect(@typeInfo(E).Enum.fields.len == 2); + e = @intToEnum(E, 12); + expect(@enumToInt(e) == 12); + e = @intToEnum(E, y); + expect(@enumToInt(e) == 52); + expect(@typeInfo(E).Enum.is_exhaustive == false); + } + }; + S.doTheTest(52); + comptime S.doTheTest(52); +} + +test "empty non-exhaustive enum" { + const S = struct { + const E = enum(u8) { + _, + }; + fn doTheTest(y: u8) void { + var e = @intToEnum(E, y); + expect(switch (e) { + _ => true, + }); + expect(@enumToInt(e) == y); + + expect(@typeInfo(E).Enum.fields.len == 0); + expect(@typeInfo(E).Enum.is_exhaustive == false); + } + }; + S.doTheTest(42); + comptime S.doTheTest(42); +} + test "enum type" { const foo1 = Foo{ .One = 13 }; const foo2 = Foo{ @@ -1057,3 +1114,8 @@ test "enum with one member default to u0 tag type" { }; comptime expect(@TagType(E0) == u0); } + +test "tagName on enum literals" { + expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); + comptime expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); +} diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig index d46cdccd0d..a738dbbbf9 100644 --- a/test/stage1/behavior/sizeof_and_typeof.zig +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -124,3 +124,14 @@ fn fn1(alpha: bool) void { test "lazy @sizeOf result is checked for definedness" { const f = fn1; } + +test "@bitSizeOf" { + expect(@bitSizeOf(u2) == 2); + expect(@bitSizeOf(u8) == @sizeOf(u8) * 8); + expect(@bitSizeOf(struct { + a: u2 + }) == 8); + expect(@bitSizeOf(packed struct { + a: u2 + }) == 2); +} diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig index d35cc8831f..9d577ec0b4 100644 --- a/test/stage1/behavior/type_info.zig +++ b/test/stage1/behavior/type_info.zig @@ -237,9 +237,11 @@ fn testStruct() void { const struct_info = @typeInfo(TestStruct); expect(@as(TypeId, struct_info) == TypeId.Struct); expect(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); - expect(struct_info.Struct.fields.len == 3); + expect(struct_info.Struct.fields.len == 4); expect(struct_info.Struct.fields[1].offset == null); expect(struct_info.Struct.fields[2].field_type == *TestStruct); + expect(struct_info.Struct.fields[2].default_value == null); + expect(struct_info.Struct.fields[3].default_value.? == 4); expect(struct_info.Struct.decls.len == 2); expect(struct_info.Struct.decls[0].is_pub); expect(!struct_info.Struct.decls[0].data.Fn.is_extern); @@ -254,6 +256,7 @@ const TestStruct = packed struct { fieldA: usize, fieldB: void, fieldC: *Self, + fieldD: u32 = 4, pub fn foo(self: *const Self) void {} }; diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index a24bee03e6..4625b7573a 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -629,3 +629,12 @@ test "union initializer generates padding only if needed" { var v = U{ .A = 532 }; expect(v.A == 532); } + +test "runtime tag name with single field" { + const U = union(enum) { + A: i32, + }; + + var v = U{ .A = 42 }; + expect(std.mem.eql(u8, @tagName(v), "A")); +} diff --git a/test/translate_c.zig b/test/translate_c.zig index 16caa6e323..df33d9b145 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -629,6 +629,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ VAL21 = 6917529027641081853, \\ VAL22 = 0, \\ VAL23 = -1, + \\ _, \\}; }); } @@ -988,8 +989,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\enum enum_ty { FOO }; , &[_][]const u8{ \\pub const FOO = @enumToInt(enum_enum_ty.FOO); - \\pub const enum_enum_ty = extern enum { + \\pub const enum_enum_ty = extern enum(c_int) { \\ FOO, + \\ _, \\}; \\pub extern var my_enum: enum_enum_ty; }); @@ -1102,28 +1104,31 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const a = @enumToInt(enum_unnamed_1.a); \\pub const b = @enumToInt(enum_unnamed_1.b); \\pub const c = @enumToInt(enum_unnamed_1.c); - \\const enum_unnamed_1 = extern enum { + \\const enum_unnamed_1 = extern enum(c_int) { \\ a, \\ b, \\ c, + \\ _, \\}; \\pub const d = enum_unnamed_1; \\pub const e = @enumToInt(enum_unnamed_2.e); \\pub const f = @enumToInt(enum_unnamed_2.f); \\pub const g = @enumToInt(enum_unnamed_2.g); - \\const enum_unnamed_2 = extern enum { + \\const enum_unnamed_2 = extern enum(c_int) { \\ e = 0, \\ f = 4, \\ g = 5, + \\ _, \\}; \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e); \\pub const i = @enumToInt(enum_unnamed_3.i); \\pub const j = @enumToInt(enum_unnamed_3.j); \\pub const k = @enumToInt(enum_unnamed_3.k); - \\const enum_unnamed_3 = extern enum { + \\const enum_unnamed_3 = extern enum(c_int) { \\ i, \\ j, \\ k, + \\ _, \\}; \\pub const struct_Baz = extern struct { \\ l: enum_unnamed_3, @@ -1132,10 +1137,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const n = @enumToInt(enum_i.n); \\pub const o = @enumToInt(enum_i.o); \\pub const p = @enumToInt(enum_i.p); - \\pub const enum_i = extern enum { + \\pub const enum_i = extern enum(c_int) { \\ n, \\ o, \\ p, + \\ _, \\}; , \\pub const Baz = struct_Baz; @@ -1563,9 +1569,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub const One = @enumToInt(enum_unnamed_1.One); \\pub const Two = @enumToInt(enum_unnamed_1.Two); - \\const enum_unnamed_1 = extern enum { + \\const enum_unnamed_1 = extern enum(c_int) { \\ One, \\ Two, + \\ _, \\}; }); @@ -1665,10 +1672,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); \\} , &[_][]const u8{ - \\pub const enum_Foo = extern enum { + \\pub const enum_Foo = extern enum(c_int) { \\ A, \\ B, \\ C, + \\ _, \\}; \\pub const SomeTypedef = c_int; \\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int { @@ -1710,9 +1718,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ y: c_int, \\}; , - \\pub const enum_Bar = extern enum { + \\pub const enum_Bar = extern enum(c_int) { \\ A, \\ B, + \\ _, \\}; \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; , @@ -1973,10 +1982,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 4; \\} , &[_][]const u8{ - \\pub const enum_SomeEnum = extern enum { + \\pub const enum_SomeEnum = extern enum(c_int) { \\ A, \\ B, \\ C, + \\ _, \\}; \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int { \\ var a = arg_a; @@ -2414,10 +2424,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const FooA = @enumToInt(enum_Foo.A); \\pub const FooB = @enumToInt(enum_Foo.B); \\pub const Foo1 = @enumToInt(enum_Foo.@"1"); - \\pub const enum_Foo = extern enum { + \\pub const enum_Foo = extern enum(c_int) { \\ A = 2, \\ B = 5, \\ @"1" = 6, + \\ _, \\}; , \\pub const Foo = enum_Foo;