authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-10-28 10:19:21+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-10-28 10:19:21+01:00
log06d9e3bc065be1614ff8e01e394a5160d7283dd7
tree13da053ff6f744a5db769af887cf87c34d7be29e
parent35e1755c996eb1cff77ff262d93302e5d6f16f9a
parent104c272ae5dc0ab2fc6f81da5918f1b8abf7e131
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #25691 from GasInfinity-Forks/x86_16-gcc

feat: init x86_16 arch via CBE

16 files changed, 96 insertions(+), 33 deletions(-)

lib/compiler_rt/int_from_float.zig+1-1
......@@ -74,7 +74,7 @@ pub inline fn bigIntFromFloat(comptime signedness: std.builtin.Signedness, resul
7474 const parts = math.frexp(a);
7575 const significand_bits_adjusted_to_handle_smin = @as(i32, significand_bits) +
7676 @intFromBool(signedness == .signed and parts.exponent == 32 * result.len);
77 const exponent = @max(parts.exponent - significand_bits_adjusted_to_handle_smin, 0);
77 const exponent: usize = @intCast(@max(parts.exponent - significand_bits_adjusted_to_handle_smin, 0));
7878 const int: I = @intFromFloat(switch (exponent) {
7979 0 => a,
8080 else => math.ldexp(parts.significand, significand_bits_adjusted_to_handle_smin),
lib/std/Io/Writer.zig+1-1
......@@ -604,7 +604,7 @@ pub fn print(w: *Writer, comptime fmt: []const u8, args: anytype) Error!void {
604604 @compileError("32 arguments max are supported per format call");
605605 }
606606
607 @setEvalBranchQuota(fmt.len * 1000);
607 @setEvalBranchQuota(@as(comptime_int, fmt.len) * 1000); // NOTE: We're upcasting as 16-bit usize overflows.
608608 comptime var arg_state: std.fmt.ArgState = .{ .args_len = fields_info.len };
609609 comptime var i = 0;
610610 comptime var literal: []const u8 = "";
lib/std/Target.zig+23-7
......@@ -1083,7 +1083,7 @@ pub fn toElfMachine(target: *const Target) std.elf.EM {
10831083 .sparc => if (target.cpu.has(.sparc, .v9)) .SPARC32PLUS else .SPARC,
10841084 .sparc64 => .SPARCV9,
10851085 .ve => .VE,
1086 .x86 => .@"386",
1086 .x86_16, .x86 => .@"386",
10871087 .x86_64 => .X86_64,
10881088 .xcore => .XCORE,
10891089 .xtensa, .xtensaeb => .XTENSA,
......@@ -1154,6 +1154,7 @@ pub fn toCoffMachine(target: *const Target) std.coff.IMAGE.FILE.MACHINE {
11541154 .ve,
11551155 .wasm32,
11561156 .wasm64,
1157 .x86_16,
11571158 .xcore,
11581159 .xtensa,
11591160 .xtensaeb,
......@@ -1376,6 +1377,7 @@ pub const Cpu = struct {
13761377 ve,
13771378 wasm32,
13781379 wasm64,
1380 x86_16,
13791381 x86,
13801382 x86_64,
13811383 xcore,
......@@ -1467,7 +1469,7 @@ pub const Cpu = struct {
14671469 .spirv32, .spirv64 => .spirv,
14681470 .ve => .ve,
14691471 .wasm32, .wasm64 => .wasm,
1470 .x86, .x86_64 => .x86,
1472 .x86_16, .x86, .x86_64 => .x86,
14711473 .xcore => .xcore,
14721474 .xtensa, .xtensaeb => .xtensa,
14731475 };
......@@ -1475,7 +1477,7 @@ pub const Cpu = struct {
14751477
14761478 pub inline fn isX86(arch: Arch) bool {
14771479 return switch (arch) {
1478 .x86, .x86_64 => true,
1480 .x86_16, .x86, .x86_64 => true,
14791481 else => false,
14801482 };
14811483 }
......@@ -1669,6 +1671,7 @@ pub const Cpu = struct {
16691671 .ve,
16701672 .wasm32,
16711673 .wasm64,
1674 .x86_16,
16721675 .x86,
16731676 .x86_64,
16741677 .xcore,
......@@ -1789,6 +1792,12 @@ pub const Cpu = struct {
17891792 .x86_interrupt,
17901793 => &.{.x86},
17911794
1795 .x86_16_cdecl,
1796 .x86_16_stdcall,
1797 .x86_16_regparmcall,
1798 .x86_16_interrupt,
1799 => &.{.x86_16},
1800
17921801 .aarch64_aapcs,
17931802 .aarch64_aapcs_darwin,
17941803 .aarch64_aapcs_win,
......@@ -1971,6 +1980,7 @@ pub const Cpu = struct {
19711980 .riscv64, .riscv64be => &riscv.cpu.generic_rv64,
19721981 .sparc64 => &sparc.cpu.v9, // SPARC can only be 64-bit from v9 and up.
19731982 .wasm32, .wasm64 => &wasm.cpu.mvp,
1983 .x86_16 => &x86.cpu.i86,
19741984 .x86 => &x86.cpu.i386,
19751985 .x86_64 => &x86.cpu.x86_64,
19761986 inline else => |a| &@field(Target, @tagName(a.family())).cpu.generic,
......@@ -2237,7 +2247,10 @@ pub fn supportsAddressSpace(
22372247
22382248 return switch (address_space) {
22392249 .generic => true,
2240 .fs, .gs, .ss => (arch == .x86_64 or arch == .x86) and (context == null or context == .pointer),
2250 .fs, .gs, .ss => (arch == .x86_64 or arch == .x86 or arch == .x86_16) and (context == null or context == .pointer),
2251 // Technically x86 can use segmentation...
2252 .far => (arch == .x86_16),
2253
22412254 .flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr, // TODO this should also check how many flash banks the cpu has
22422255 .cog, .hub => arch == .propeller,
22432256 .lut => arch == .propeller and std.Target.propeller.featureSetHas(target.cpu.features, .p2),
......@@ -2800,6 +2813,7 @@ pub fn ptrBitWidth_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) u16 {
28002813 return switch (cpu_arch) {
28012814 .avr,
28022815 .msp430,
2816 .x86_16,
28032817 => 16,
28042818
28052819 .arc,
......@@ -3013,7 +3027,7 @@ pub fn cTypeByteSize(t: *const Target, c_type: CType) u16 {
30133027pub fn cTypeBitSize(target: *const Target, c_type: CType) u16 {
30143028 switch (target.os.tag) {
30153029 .freestanding, .other => switch (target.cpu.arch) {
3016 .msp430 => switch (c_type) {
3030 .msp430, .x86_16 => switch (c_type) {
30173031 .char => return 8,
30183032 .short, .ushort, .int, .uint => return 16,
30193033 .float, .long, .ulong => return 32,
......@@ -3369,6 +3383,7 @@ pub fn cTypeAlignment(target: *const Target, c_type: CType) u16 {
33693383 std.math.ceilPowerOfTwoAssert(u16, (cTypeBitSize(target, c_type) + 7) / 8),
33703384 @as(u16, switch (target.cpu.arch) {
33713385 .msp430,
3386 .x86_16,
33723387 => 2,
33733388
33743389 .arc,
......@@ -3476,7 +3491,7 @@ pub fn cTypePreferredAlignment(target: *const Target, c_type: CType) u16 {
34763491 return @min(
34773492 std.math.ceilPowerOfTwoAssert(u16, (cTypeBitSize(target, c_type) + 7) / 8),
34783493 @as(u16, switch (target.cpu.arch) {
3479 .msp430 => 2,
3494 .x86_16, .msp430 => 2,
34803495
34813496 .arc,
34823497 .arceb,
......@@ -3548,7 +3563,7 @@ pub fn cMaxIntAlignment(target: *const Target) u16 {
35483563 return switch (target.cpu.arch) {
35493564 .avr => 1,
35503565
3551 .msp430 => 2,
3566 .msp430, .x86_16 => 2,
35523567
35533568 .arc,
35543569 .arceb,
......@@ -3625,6 +3640,7 @@ pub fn cCallingConvention(target: *const Target) ?std.builtin.CallingConvention
36253640 .windows, .uefi => .{ .x86_win = .{} },
36263641 else => .{ .x86_sysv = .{} },
36273642 },
3643 .x86_16 => .{ .x86_16_cdecl = .{} },
36283644 .aarch64, .aarch64_be => if (target.os.tag.isDarwin())
36293645 .{ .aarch64_aapcs_darwin = .{} }
36303646 else switch (target.os.tag) {
lib/std/Target/x86.zig+5
......@@ -3081,6 +3081,11 @@ pub const cpu = struct {
30813081 .xsaveopt,
30823082 }),
30833083 };
3084 pub const @"i86": CpuModel = .{
3085 .name = "i86",
3086 .llvm_name = null,
3087 .features = featureSet(&[_]Feature{}),
3088 };
30843089 pub const @"i386": CpuModel = .{
30853090 .name = "i386",
30863091 .llvm_name = "i386",
lib/std/builtin.zig+11
......@@ -223,6 +223,13 @@ pub const CallingConvention = union(enum(u8)) {
223223 x86_vectorcall: CommonOptions,
224224 x86_interrupt: CommonOptions,
225225
226 // Calling conventions for the `x86_16` architecture.
227
228 x86_16_cdecl: CommonOptions,
229 x86_16_stdcall: CommonOptions,
230 x86_16_regparmcall: CommonOptions,
231 x86_16_interrupt: CommonOptions,
232
226233 // Calling conventions for the `aarch64` and `aarch64_be` architectures.
227234 aarch64_aapcs: CommonOptions,
228235 aarch64_aapcs_darwin: CommonOptions,
......@@ -523,6 +530,10 @@ pub const AddressSpace = enum(u5) {
523530 fs,
524531 ss,
525532
533 // x86_16 extra address spaces.
534 /// Allows addressing the entire address space by storing both segment and offset.
535 far,
536
526537 // GPU address spaces.
527538 global,
528539 constant,
lib/std/builtin/assembly.zig+1-1
......@@ -1,5 +1,5 @@
11pub const Clobbers = switch (@import("builtin").cpu.arch) {
2 .x86, .x86_64 => packed struct {
2 .x86_16, .x86, .x86_64 => packed struct {
33 /// Whether the inline assembly code may perform stores to memory
44 /// addresses other than those derived from input pointer provenance.
55 memory: bool = false,
lib/std/os/windows.zig+22-19
......@@ -4634,25 +4634,28 @@ pub const TEB = extern struct {
46344634};
46354635
46364636comptime {
4637 // Offsets taken from WinDbg info and Geoff Chappell[1] (RIP)
4638 // [1]: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm
4639 assert(@offsetOf(TEB, "NtTib") == 0x00);
4640 if (@sizeOf(usize) == 4) {
4641 assert(@offsetOf(TEB, "EnvironmentPointer") == 0x1C);
4642 assert(@offsetOf(TEB, "ClientId") == 0x20);
4643 assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x28);
4644 assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x2C);
4645 assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x30);
4646 assert(@offsetOf(TEB, "LastErrorValue") == 0x34);
4647 assert(@offsetOf(TEB, "TlsSlots") == 0xe10);
4648 } else if (@sizeOf(usize) == 8) {
4649 assert(@offsetOf(TEB, "EnvironmentPointer") == 0x38);
4650 assert(@offsetOf(TEB, "ClientId") == 0x40);
4651 assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x50);
4652 assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x58);
4653 assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60);
4654 assert(@offsetOf(TEB, "LastErrorValue") == 0x68);
4655 assert(@offsetOf(TEB, "TlsSlots") == 0x1480);
4637 // XXX: Without this check we cannot use `std.Io.Writer` on 16-bit platforms. `std.fmt.bufPrint` will hit the unreachable in `PEB.GdiHandleBuffer` without this guard.
4638 if (builtin.os.tag == .windows) {
4639 // Offsets taken from WinDbg info and Geoff Chappell[1] (RIP)
4640 // [1]: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm
4641 assert(@offsetOf(TEB, "NtTib") == 0x00);
4642 if (@sizeOf(usize) == 4) {
4643 assert(@offsetOf(TEB, "EnvironmentPointer") == 0x1C);
4644 assert(@offsetOf(TEB, "ClientId") == 0x20);
4645 assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x28);
4646 assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x2C);
4647 assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x30);
4648 assert(@offsetOf(TEB, "LastErrorValue") == 0x34);
4649 assert(@offsetOf(TEB, "TlsSlots") == 0xe10);
4650 } else if (@sizeOf(usize) == 8) {
4651 assert(@offsetOf(TEB, "EnvironmentPointer") == 0x38);
4652 assert(@offsetOf(TEB, "ClientId") == 0x40);
4653 assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x50);
4654 assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x58);
4655 assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60);
4656 assert(@offsetOf(TEB, "LastErrorValue") == 0x68);
4657 assert(@offsetOf(TEB, "TlsSlots") == 0x1480);
4658 }
46564659 }
46574660}
46584661
lib/std/zig/system.zig+5
......@@ -374,6 +374,11 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
374374 // However, the "mode" flags can be used as overrides, so if the user explicitly
375375 // sets one of them, that takes precedence.
376376 switch (query_cpu_arch) {
377 .x86_16 => {
378 cpu.features.addFeature(
379 @intFromEnum(Target.x86.Feature.@"16bit_mode"),
380 );
381 },
377382 .x86 => {
378383 if (!Target.x86.featureSetHasAny(query.cpu_features_add, .{
379384 .@"16bit_mode", .@"32bit_mode",
lib/std/zig/target.zig+3-2
......@@ -472,8 +472,9 @@ fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
472472 }
473473}
474474
475pub fn intByteSize(target: *const std.Target, bits: u16) u19 {
476 return std.mem.alignForward(u19, @intCast((@as(u17, bits) + 7) / 8), intAlignment(target, bits));
475pub fn intByteSize(target: *const std.Target, bits: u16) u16 {
476 const previous_aligned = std.mem.alignBackward(u16, bits, 8);
477 return std.mem.alignForward(u16, @divExact(previous_aligned, 8) + @intFromBool(previous_aligned != bits), intAlignment(target, bits));
477478}
478479
479480pub fn intAlignment(target: *const std.Target, bits: u16) u16 {
lib/zig.h+6-1
......@@ -74,6 +74,9 @@
7474#elif defined (__x86_64__) || (defined(zig_msvc) && defined(_M_X64))
7575#define zig_x86_64
7676#define zig_x86
77#elif defined(__I86__)
78#define zig_x86_16
79#define zig_x86
7780#endif
7881
7982#if defined(zig_msvc) || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
......@@ -400,6 +403,8 @@
400403#define zig_trap() __asm__ volatile("j 0x2")
401404#elif defined(zig_sparc)
402405#define zig_trap() __asm__ volatile("illtrap")
406#elif defined(zig_x86_16)
407#define zig_trap() __asm__ volatile("int $0x3")
403408#elif defined(zig_x86)
404409#define zig_trap() __asm__ volatile("ud2")
405410#else
......@@ -4219,7 +4224,7 @@ static inline void zig_loongarch_cpucfg(uint32_t word, uint32_t* result) {
42194224#endif
42204225}
42214226
4222#elif defined(zig_x86)
4227#elif defined(zig_x86) && !defined(zig_x86_16)
42234228
42244229static inline void zig_x86_cpuid(uint32_t leaf_id, uint32_t subid, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) {
42254230#if defined(zig_msvc)
src/Sema.zig+1
......@@ -9034,6 +9034,7 @@ pub fn handleExternLibName(
90349034/// Any calling conventions not included here are either not yet verified to work with variadic
90359035/// functions or there are no more other calling conventions that support variadic functions.
90369036const calling_conventions_supporting_var_args = [_]std.builtin.CallingConvention.Tag{
9037 .x86_16_cdecl,
90379038 .x86_64_sysv,
90389039 .x86_64_x32,
90399040 .x86_64_win,
src/Zcu.zig+4
......@@ -4406,6 +4406,10 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
44064406 }
44074407 }
44084408 break :ok switch (cc) {
4409 .x86_16_cdecl,
4410 .x86_16_stdcall,
4411 .x86_16_regparmcall,
4412 .x86_16_interrupt,
44094413 .x86_64_sysv,
44104414 .x86_64_win,
44114415 .x86_64_vectorcall,
src/codegen/c.zig+4-1
......@@ -8055,9 +8055,11 @@ fn toCallingConvention(cc: std.builtin.CallingConvention, zcu: *Zcu) ?[]const u8
80558055 return switch (cc) {
80568056 .auto, .naked => null,
80578057
8058 .x86_16_cdecl => "cdecl",
8059 .x86_16_regparmcall => "regparmcall",
80588060 .x86_64_sysv, .x86_sysv => "sysv_abi",
80598061 .x86_64_win, .x86_win => "ms_abi",
8060 .x86_stdcall => "stdcall",
8062 .x86_16_stdcall, .x86_stdcall => "stdcall",
80618063 .x86_fastcall => "fastcall",
80628064 .x86_thiscall => "thiscall",
80638065
......@@ -8127,6 +8129,7 @@ fn toCallingConvention(cc: std.builtin.CallingConvention, zcu: *Zcu) ?[]const u8
81278129 .csky_interrupt,
81288130 .m68k_interrupt,
81298131 .msp430_interrupt,
8132 .x86_16_interrupt,
81308133 .x86_interrupt,
81318134 .x86_64_interrupt,
81328135 => "interrupt",
src/codegen/llvm.zig+7
......@@ -117,6 +117,7 @@ pub fn targetTriple(allocator: Allocator, target: *const std.Target) ![]const u8
117117 .propeller,
118118 .sh,
119119 .sheb,
120 .x86_16,
120121 .xtensaeb,
121122 => unreachable, // Gated by hasLlvmSupport().
122123 };
......@@ -493,6 +494,7 @@ pub fn dataLayout(target: *const std.Target) []const u8 {
493494 .propeller,
494495 .sh,
495496 .sheb,
497 .x86_16,
496498 .xtensaeb,
497499 => unreachable, // Gated by hasLlvmSupport().
498500 };
......@@ -11902,6 +11904,10 @@ fn toLlvmCallConvTag(cc_tag: std.builtin.CallingConvention.Tag, target: *const s
1190211904
1190311905 // All the calling conventions which LLVM does not have a general representation for.
1190411906 // Note that these are often still supported through the `cCallingConvention` path above via `ccc`.
11907 .x86_16_cdecl,
11908 .x86_16_stdcall,
11909 .x86_16_regparmcall,
11910 .x86_16_interrupt,
1190511911 .x86_sysv,
1190611912 .x86_win,
1190711913 .x86_thiscall_mingw,
......@@ -13131,6 +13137,7 @@ pub fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void {
1313113137 .propeller,
1313213138 .sh,
1313313139 .sheb,
13140 .x86_16,
1313413141 .xtensaeb,
1313513142 => unreachable,
1313613143 }
src/codegen/spirv/Module.zig+1
......@@ -927,6 +927,7 @@ pub fn storageClass(module: *Module, as: std.builtin.AddressSpace) spec.StorageC
927927 .gs,
928928 .fs,
929929 .ss,
930 .far,
930931 .param,
931932 .flash,
932933 .flash1,
src/target.zig+1
......@@ -227,6 +227,7 @@ pub fn hasLlvmSupport(target: *const std.Target, ofmt: std.Target.ObjectFormat)
227227 .propeller,
228228 .sh,
229229 .sheb,
230 .x86_16,
230231 .xtensaeb,
231232 => false,
232233 };