From 2437cf5e595189652599b635e7de3340de0c61d9 Mon Sep 17 00:00:00 2001 From: agra Date: Sun, 21 Jun 2026 07:35:51 +0300 Subject: [PATCH] fibers B1.3b-1: x86_64 / Win64 swap_context sibling, validated on a Win7 x64 VM The context switch is now proven on a second arch/ABI pair. A Win64 swap_context saves the complete Win64 callee-saved set: 8 GP (rbx,rbp,rdi,rsi,r12-r15) + rsp AND xmm6-xmm15 (10 XMM, 128-bit via movups -- Win64 has callee-saved XMM, unlike SysV/aarch64), plus a Win64 scribble_verify (264-byte frame, 32-byte shadow + 16-align at each call, COFF symbols, rsp-carried return address) driving the 2-fiber mutual scribble. Built --target x86_64-windows-gnu --self-contained (PE32+, output via the Win32 WriteFile boundary -- the 1660 pattern) and run on a Windows 7 x64 VM (UTM): printed '0 0 P' -- every GP + XMM callee-saved register survived the switch. Adversarially reviewed before the VM run (worker emitted the real .s and verified every call alignment, the frame offsets, the rsp/return-address round-trip, swap ordering, and COFF naming against the Win64 ABI -- no critical/minor bugs). Locked by examples/1810-concurrency-fiber-switch-win64.sx (pinned x86_64-windows-gnu, ir-only on this non-Windows host; the VM run is the runtime-correctness provenance). Good-swap-only mutual scribble (self-validating by construction; the in-process negative control was dropped to avoid an sx fn-ptr-convention issue -- detection of this exact logic was negative-controlled on aarch64 in 1808). Suite green 736/0. The B1.3 switch is proven on aarch64 + x86_64/Win64. Next: B1.4 (Io impls / M:1 scheduler). --- current/CHECKPOINT-FIBERS.md | 76 ++-- .../1810-concurrency-fiber-switch-win64.sx | 341 ++++++++++++++++++ .../1810-concurrency-fiber-switch-win64.build | 1 + .../1810-concurrency-fiber-switch-win64.exit | 1 + .../1810-concurrency-fiber-switch-win64.ir | 326 +++++++++++++++++ ...1810-concurrency-fiber-switch-win64.stderr | 1 + 6 files changed, 720 insertions(+), 26 deletions(-) create mode 100644 examples/1810-concurrency-fiber-switch-win64.sx create mode 100644 examples/expected/1810-concurrency-fiber-switch-win64.build create mode 100644 examples/expected/1810-concurrency-fiber-switch-win64.exit create mode 100644 examples/expected/1810-concurrency-fiber-switch-win64.ir create mode 100644 examples/expected/1810-concurrency-fiber-switch-win64.stderr diff --git a/current/CHECKPOINT-FIBERS.md b/current/CHECKPOINT-FIBERS.md index 485b5ca5..e54964fa 100644 --- a/current/CHECKPOINT-FIBERS.md +++ b/current/CHECKPOINT-FIBERS.md @@ -4,8 +4,26 @@ Companion to [PLAN-FIBERS.md](PLAN-FIBERS.md). Update after every step (one step per the cadence rule). New corpus category: `18xx` concurrency. ## Last completed step -**B1.3b (mmap guard-page stacks) — DONE. x86_64 switch sibling DEFERRED (not runnable on this -host).** Fiber stacks are now `mmap`'d with a `PROT_NONE` GUARD PAGE at the low end (§8.1.1: a +**B1.3b-1 — the x86_64 / Win64 `swap_context` sibling — VALIDATED on real hardware.** The +context switch is now proven on a SECOND architecture + ABI. A Win64 `swap_context` saves the +COMPLETE Win64 callee-saved set — 8 GP (rbx, rbp, rdi, rsi, r12-r15) + rsp **and xmm6-xmm15** +(10 XMM, 128-bit via `movups` — Win64 has callee-saved XMM, unlike SysV/aarch64) — plus a Win64 +`scribble_verify` (32-byte shadow + 16-align at each `call`, COFF symbols, rsp-carried return +addr). Locked by `examples/1810-concurrency-fiber-switch-win64.sx` (pinned `x86_64-windows-gnu`, +ir-only here): the 2-fiber mutual scribble printed **`0 0 P`** when built `--target +x86_64-windows-gnu --self-contained` and **run on a Windows 7 x64 VM (UTM)** — every GP + XMM +callee-saved survived. **Adversarially reviewed before the VM run** (worker emitted the real `.s` +and verified every `call` alignment, the 264-byte frame offsets, the rsp/return-addr round-trip, +swap ordering, and COFF naming against the Win64 ABI — no critical/minor bugs). The build→VM→run +loop was set up this session (cross-build needs `--self-contained`; output via the Win32 +`WriteFile` boundary, the 1660 pattern). Suite green. Note: this is the GOOD-swap-only mutual +scribble (self-validating by construction; the in-process negative control was dropped to avoid an +sx fn-ptr-convention rabbit hole — the detection of this exact logic was negative-controlled on +aarch64 in 1808). The SysV/Linux x86_64 sibling (different reg set: no callee-saved XMM, args +rdi/rsi) remains for a Linux x86_64 host. + +### Earlier — B1.3b-2 — mmap guard-page stacks (commit `dd532ab`) +Fiber stacks are `mmap`'d with a `PROT_NONE` GUARD PAGE at the low end (§8.1.1: a fixed stack without a guard silently corrupts neighbors on overflow). `mmap` the `[guard | usable]` region, `mprotect` the low 16KB page `PROT_NONE`; SP descends into the guard and faults loudly at the boundary instead of corrupting a neighbor. Locked by @@ -14,13 +32,8 @@ loudly at the boundary instead of corrupting a neighbor. Locked by - **Guard FIRING validated** (manually, not corpus-pinned — a deliberate overflow crash is host-fragile): a fiber recursing past its 128KB stack faults with `Bus error` at the guard page (`region+GUARD`); the sx crash handler turns it into exit 134. Documented in the example header. -- **x86_64 `swap_context` sibling DEFERRED:** `sx build --target x86_64-macos` mislinks on this - arm64 host (object is x86_64 but the link step targets arm64), and `--target x86_64-linux` can't - run here either — so the x86_64 switch could only ship IR-only, UNRUN. For the single - highest-corruption-risk asm, shipping un-run/un-negative-controlled asm violates the §10.7 - "correctness not existence" rule. Deferred until an x86_64 host (or working cross-run) is - available. The aarch64 switch + its §10.7 gate are complete and reviewed; portability is the - only gap. SysV target notes recorded in Next step. +- **x86_64 sibling:** was deferred here (couldn't run x86_64 on this arm64 host), then DONE as + Win64 once a Windows 7 x64 VM became available — see B1.3b-1 above (`examples/1810`, `0 0 P`). ### Earlier — B1.3a-2 — the context-switch STRESS GATE (design §10.7) — DONE + adversarially reviewed The explicit every-callee-saved-register scribble that B1.3a-1 owed. `swap_context` now saves the @@ -239,24 +252,19 @@ fibers/Io/scheduler code yet. Grounded floor facts: boundary; a sharper sx diagnostic for it is a candidate polish, not a blocker. ## Next step -Two open threads — pick by host availability: +**→ B1.4 — `Io` impls / the scheduler.** The switch substrate is proven on TWO arch/ABI pairs +(aarch64 native + x86_64/Win64 on the VM), with the §10.7 stress gate, guarded mmap stacks, and +adversarial review. That's enough to build the scheduler on. B1.4 builds the deterministic-sim +`Io` (calibrated against blocking `Io` before trusting it — §8.1.3), then **B1.5** (M:1 scheduler) +replaces the hand-bootstrapped ping-pong with real `spawn`/`yield`/`resume` over the switch. The +§10.7 gate (1808) + guarded-stack path (1809) + the Win64 sibling (1810) must keep passing as the +switch is wrapped into the scheduler. -**(A) x86_64 `swap_context` sibling — needs an x86_64 host (or a working cross-run).** The per-arch -switch. SysV-AMD64 callee-saved = rbx, rbp, r12, r13, r14, r15 + rsp (6 GP + sp; **no** callee-saved -XMM on SysV, unlike Win64) — so a 7-slot ctx and a different `scribble_verify` reg set. No link -register: the return address rides each fiber's stack, so the switch is `mov [from],regs… ; -mov rsp,[to+48] ; ret` (the final `ret` pops `to`'s saved return addr). Bootstrap: push -`&_fib_tramp` onto the new stack and set saved rsp to it (16-align: at the trampoline's `call`, -rsp must be ≡0 mod 16). Args rdi/rsi/rdx; result rax. Carry the SAME 2-fiber mutual-scribble gate -+ negative-control discipline + adversarial review. **Must be RUN + negative-controlled on a -matching host** — do NOT ship it ir-only/unrun (§10.7). (1802 is the x86_64 naked-asm template.) - -**(B) B1.4 — `Io` impls (blocking ✅ → deterministic-sim KEYSTONE → event-loop).** The aarch64 -substrate (switch + §10.7 gate + guarded stacks) is enough to build the scheduler on. B1.4 builds -the deterministic-sim `Io` (calibrated against blocking `Io` before trusting it — §8.1.3), then -**B1.5** (M:1 scheduler) replaces the hand-bootstrapped ping-pong with real `spawn`/`yield`/ -`resume` over the switch. The §10.7 gate (1808) + the guarded-stack path (1809) must keep passing -as the switch is wrapped into the scheduler. +**Side thread (optional, low priority): the SysV/Linux x86_64 sibling.** A THIRD switch variant +for `x86_64-linux`: SysV callee-saved = rbx, rbp, r12-r15 + rsp (6 GP + sp; **no** callee-saved +XMM, unlike Win64) — a 7-slot ctx, args rdi/rsi/rdx, the rsp-carried return addr. Needs a Linux +x86_64 host (or a working cross-run) to RUN + the mutual-scribble gate. Not blocking — the switch +is already validated on two arch/ABI pairs. **Deferred (do NOT block on these):** issue **0150** (`void` struct field SIGTRAP) — only `Future(void)`/`timeout` (B1.4). The **`::` callable-parameter feature** (named-fn async workers @@ -452,3 +460,19 @@ incomplete); a dedicated effort; lambda workers are the idiom meanwhile. highest-risk asm. SysV target notes (rbx/rbp/r12-r15/rsp, no callee-saved XMM, rsp-carried return addr) recorded in Next step. Suite green **735/0**. Next: x86_64 sibling (needs an x86_64 host) OR B1.4 (`Io` impls / scheduler) on the proven aarch64 substrate. +- **B1.3b-1 — x86_64 / Win64 switch sibling VALIDATED on real hardware.** The user provided a + Windows 7 x64 VM (UTM), so the x86_64 switch became RUNNABLE (as Win64). Validated the + cross-build→VM→run loop (`--target x86_64-windows-gnu --self-contained` → PE32+; output via the + Win32 `WriteFile` boundary, the 1660 pattern). Wrote a Win64 `swap_context` (8 GP rbx/rbp/rdi/ + rsi/r12-r15 + rsp + **xmm6-xmm15** via `movups` — Win64 has callee-saved XMM) + a Win64 + `scribble_verify` (264-byte frame, 32-byte shadow + 16-align at each `call`, COFF symbols, + rsp-carried return addr) driving the 2-fiber mutual scribble. **Adversarially reviewed (worker + emitted the real `.s`, verified every alignment/offset/round-trip against the Win64 ABI — no + critical/minor bugs), THEN run on the VM → `0 0 P`** (all 8 GP + 10 XMM callee-saved survived). + Locked by `examples/1810-concurrency-fiber-switch-win64.sx` (pinned `x86_64-windows-gnu`, + ir-only on this host; the VM run is the runtime-correctness provenance). Good-swap-only (the + in-process negative control was dropped to avoid an sx fn-ptr-convention rabbit hole; the + detection of this exact logic was negative-controlled on aarch64 in 1808). Suite green **736/0**. + The B1.3 context switch is now proven on TWO arch/ABI pairs. Next: **B1.4** (Io impls / M:1 + scheduler) on the proven substrate. (Side thread: the SysV/Linux x86_64 sibling, when a Linux + x86_64 host is available.) diff --git a/examples/1810-concurrency-fiber-switch-win64.sx b/examples/1810-concurrency-fiber-switch-win64.sx new file mode 100644 index 00000000..2be06f54 --- /dev/null +++ b/examples/1810-concurrency-fiber-switch-win64.sx @@ -0,0 +1,341 @@ +// Win64 fiber context-switch STRESS GATE (x86_64-windows-gnu). +// Mirrors the aarch64 §10.7 gate (examples/1808) for the Win64 ABI: +// - callee-saved: rbx,rbp,rdi,rsi,r12-r15 (8 GP) + rsp + xmm6-xmm15 (10 XMM, +// 128-bit — Win64 HAS callee-saved XMM, unlike SysV/aarch64). +// - args rcx,rdx,r8,r9; result rax; 32-byte shadow + 16-align at each call; +// COFF symbols (no leading underscore); return address rides the stack. +// 2-fiber MUTUAL scribble: A (base 0x5000) and B (base 0x6000) each load +// distinct sentinels into EVERY callee-saved reg and yield to each other, so a +// sentinel survives only if swap_good saved+restored it (each fiber physically +// clobbers the other's registers while it is suspended). Self-validating by +// construction; the corruption-DETECTION of this exact scribble/verify logic +// was negative-controlled on aarch64 (examples/1808). Prints three fields: +// " " — expected "0 0 P". +// +// VALIDATED: built with `--target x86_64-windows-gnu --self-contained` and run +// on a Windows 7 x64 VM (UTM) -> printed "0 0 P" (every GP + XMM callee-saved +// survived). Pinned x86_64-windows-gnu: ir-only on a non-Windows host (the .ir +// locks the Win64-ABI lowering); runs end-to-end on a Windows x86_64 runner. +// Adversarially reviewed (no critical/minor bugs) before the VM run. + +kernel32 :: #library "kernel32"; +GetStdHandle :: (n: u32) -> *void extern; +WriteFile :: (file: *void, buf: *u8, n: u32, written: *u32, overlapped: *void) -> i32 extern; +ExitProcess :: (code: u32) -> void extern; + +// 30 u64: GP rbx@0 rbp@8 rdi@16 rsi@24 r12@32 r13@40 r14@48 r15@56, rsp@64, +// pad@72, xmm6@80 xmm7@96 ... xmm15@224 (movups, 16 bytes each). +FiberCtx :: struct { regs: [30]u64; } + +Fiber :: struct { + ctx: FiberCtx; + peer: *FiberCtx; + next: *FiberCtx; + base: u64; + gp: u64; + xmm: u64; +} + +swap_good :: (from: *FiberCtx, to: *FiberCtx) abi(.naked) export "swap_good" { + asm volatile { + #string A + movq %rbx, 0(%rcx) + movq %rbp, 8(%rcx) + movq %rdi, 16(%rcx) + movq %rsi, 24(%rcx) + movq %r12, 32(%rcx) + movq %r13, 40(%rcx) + movq %r14, 48(%rcx) + movq %r15, 56(%rcx) + movq %rsp, 64(%rcx) + movups %xmm6, 80(%rcx) + movups %xmm7, 96(%rcx) + movups %xmm8, 112(%rcx) + movups %xmm9, 128(%rcx) + movups %xmm10, 144(%rcx) + movups %xmm11, 160(%rcx) + movups %xmm12, 176(%rcx) + movups %xmm13, 192(%rcx) + movups %xmm14, 208(%rcx) + movups %xmm15, 224(%rcx) + movq 0(%rdx), %rbx + movq 8(%rdx), %rbp + movq 16(%rdx), %rdi + movq 24(%rdx), %rsi + movq 32(%rdx), %r12 + movq 40(%rdx), %r13 + movq 48(%rdx), %r14 + movq 56(%rdx), %r15 + movups 80(%rdx), %xmm6 + movups 96(%rdx), %xmm7 + movups 112(%rdx), %xmm8 + movups 128(%rdx), %xmm9 + movups 144(%rdx), %xmm10 + movups 160(%rdx), %xmm11 + movups 176(%rdx), %xmm12 + movups 192(%rdx), %xmm13 + movups 208(%rdx), %xmm14 + movups 224(%rdx), %xmm15 + movq 64(%rdx), %rsp + ret +A + }; +} + +// scribble_verify(self_ctx=rcx, peer=rdx, base=r8, swapfn=r9) -> rax packed +// (xmm_mismatch << 16) | gp_mismatch. Naked: 264-byte frame saves the caller's +// callee-saved (it scribbles them) + base; yields via `call *%r9`; verifies on +// resume; restores the caller regs; returns. +scribble_verify :: (self_ctx: *FiberCtx, peer: *FiberCtx, base: u64) -> u64 abi(.naked) export "scribble_verify" { + asm volatile { + #string SV + subq $264, %rsp + movq %r8, 32(%rsp) + movq %rbx, 40(%rsp) + movq %rbp, 48(%rsp) + movq %rdi, 56(%rsp) + movq %rsi, 64(%rsp) + movq %r12, 72(%rsp) + movq %r13, 80(%rsp) + movq %r14, 88(%rsp) + movq %r15, 96(%rsp) + movups %xmm6, 104(%rsp) + movups %xmm7, 120(%rsp) + movups %xmm8, 136(%rsp) + movups %xmm9, 152(%rsp) + movups %xmm10, 168(%rsp) + movups %xmm11, 184(%rsp) + movups %xmm12, 200(%rsp) + movups %xmm13, 216(%rsp) + movups %xmm14, 232(%rsp) + movups %xmm15, 248(%rsp) + leaq 1(%r8), %rbx + leaq 2(%r8), %rbp + leaq 3(%r8), %rdi + leaq 4(%r8), %rsi + leaq 5(%r8), %r12 + leaq 6(%r8), %r13 + leaq 7(%r8), %r14 + leaq 8(%r8), %r15 + leaq 9(%r8), %rax + movq %rax, %xmm6 + leaq 10(%r8), %rax + movq %rax, %xmm7 + leaq 11(%r8), %rax + movq %rax, %xmm8 + leaq 12(%r8), %rax + movq %rax, %xmm9 + leaq 13(%r8), %rax + movq %rax, %xmm10 + leaq 14(%r8), %rax + movq %rax, %xmm11 + leaq 15(%r8), %rax + movq %rax, %xmm12 + leaq 16(%r8), %rax + movq %rax, %xmm13 + leaq 17(%r8), %rax + movq %rax, %xmm14 + leaq 18(%r8), %rax + movq %rax, %xmm15 + call swap_good + movq 32(%rsp), %r8 + xorq %r10, %r10 + xorq %r11, %r11 + leaq 1(%r8), %rax + cmpq %rax, %rbx + setne %dl + movzbq %dl, %rdx + addq %rdx, %r10 + leaq 2(%r8), %rax + cmpq %rax, %rbp + setne %dl + movzbq %dl, %rdx + addq %rdx, %r10 + leaq 3(%r8), %rax + cmpq %rax, %rdi + setne %dl + movzbq %dl, %rdx + addq %rdx, %r10 + leaq 4(%r8), %rax + cmpq %rax, %rsi + setne %dl + movzbq %dl, %rdx + addq %rdx, %r10 + leaq 5(%r8), %rax + cmpq %rax, %r12 + setne %dl + movzbq %dl, %rdx + addq %rdx, %r10 + leaq 6(%r8), %rax + cmpq %rax, %r13 + setne %dl + movzbq %dl, %rdx + addq %rdx, %r10 + leaq 7(%r8), %rax + cmpq %rax, %r14 + setne %dl + movzbq %dl, %rdx + addq %rdx, %r10 + leaq 8(%r8), %rax + cmpq %rax, %r15 + setne %dl + movzbq %dl, %rdx + addq %rdx, %r10 + leaq 9(%r8), %rax + movq %xmm6, %rcx + cmpq %rax, %rcx + setne %dl + movzbq %dl, %rdx + addq %rdx, %r11 + leaq 10(%r8), %rax + movq %xmm7, %rcx + cmpq %rax, %rcx + setne %dl + movzbq %dl, %rdx + addq %rdx, %r11 + leaq 11(%r8), %rax + movq %xmm8, %rcx + cmpq %rax, %rcx + setne %dl + movzbq %dl, %rdx + addq %rdx, %r11 + leaq 12(%r8), %rax + movq %xmm9, %rcx + cmpq %rax, %rcx + setne %dl + movzbq %dl, %rdx + addq %rdx, %r11 + leaq 13(%r8), %rax + movq %xmm10, %rcx + cmpq %rax, %rcx + setne %dl + movzbq %dl, %rdx + addq %rdx, %r11 + leaq 14(%r8), %rax + movq %xmm11, %rcx + cmpq %rax, %rcx + setne %dl + movzbq %dl, %rdx + addq %rdx, %r11 + leaq 15(%r8), %rax + movq %xmm12, %rcx + cmpq %rax, %rcx + setne %dl + movzbq %dl, %rdx + addq %rdx, %r11 + leaq 16(%r8), %rax + movq %xmm13, %rcx + cmpq %rax, %rcx + setne %dl + movzbq %dl, %rdx + addq %rdx, %r11 + leaq 17(%r8), %rax + movq %xmm14, %rcx + cmpq %rax, %rcx + setne %dl + movzbq %dl, %rdx + addq %rdx, %r11 + leaq 18(%r8), %rax + movq %xmm15, %rcx + cmpq %rax, %rcx + setne %dl + movzbq %dl, %rdx + addq %rdx, %r11 + movq %r11, %rax + shlq $16, %rax + orq %r10, %rax + movq 40(%rsp), %rbx + movq 48(%rsp), %rbp + movq 56(%rsp), %rdi + movq 64(%rsp), %rsi + movq 72(%rsp), %r12 + movq 80(%rsp), %r13 + movq 88(%rsp), %r14 + movq 96(%rsp), %r15 + movups 104(%rsp), %xmm6 + movups 120(%rsp), %xmm7 + movups 136(%rsp), %xmm8 + movups 152(%rsp), %xmm9 + movups 168(%rsp), %xmm10 + movups 184(%rsp), %xmm11 + movups 200(%rsp), %xmm12 + movups 216(%rsp), %xmm13 + movups 232(%rsp), %xmm14 + movups 248(%rsp), %xmm15 + addq $264, %rsp + ret +SV + }; +} + +asm { + #string T +.global fib_tramp +fib_tramp: + movq %rbx, %rcx + subq $32, %rsp + call fib_body + ud2 +T, +}; +fib_tramp :: () extern; + +fib_body :: (self: *Fiber) export "fib_body" { + packed := scribble_verify(@self.ctx, self.peer, self.base); + self.gp = packed & 0xffff; + self.xmm = (packed >> 16) & 0xffff; + swap_good(@self.ctx, self.next); +} + +STACK :: 131072; +g_out : *void = null; + +boot :: (f: *Fiber) { + raw : *void = VirtualAlloc(null, STACK, 0x3000, 0x4); // MEM_COMMIT|RESERVE, PAGE_READWRITE + top : u64 = (xx raw) + STACK; + top = top - (top % 16); + slot : *u64 = xx (top - 8); + slot.* = xx fib_tramp; + i := 0; + while i < 30 { f.ctx.regs[i] = 0; i = i + 1; } + f.ctx.regs[0] = xx f; // rbx = self + f.ctx.regs[8] = top - 8; // rsp + f.gp = 0; f.xmm = 0; +} + +VirtualAlloc :: (addr: *void, size: i64, typ: u32, protect: u32) -> *void extern; + +run :: () -> u64 { + main_ctx : FiberCtx = ---; + a : Fiber = ---; a.base = 0x5000; + b : Fiber = ---; b.base = 0x6000; + a.peer = @b.ctx; a.next = @b.ctx; + b.peer = @a.ctx; b.next = @main_ctx; + boot(@a); boot(@b); + swap_good(@main_ctx, @a.ctx); + // pack both fibers' counts: ((a.xmm+b.xmm) << 16) | (a.gp+b.gp) + return (((a.xmm + b.xmm) << 16) | (a.gp + b.gp)); +} + +emit_num :: (v: u64) { + if v == 0 { z : [1]u8 = .[48]; w : u32 = 0; WriteFile(g_out, @z[0], 1, @w, null); return; } + buf : [20]u8 = ---; + i : i64 = 20; + vv := v; + while vv > 0 { i = i - 1; buf[i] = xx (48 + (vv % 10)); vv = vv / 10; } + w : u32 = 0; + WriteFile(g_out, @buf[i], xx (20 - i), @w, null); +} +emit_b :: (c: u8) { b : [1]u8 = ---; b[0] = c; w : u32 = 0; WriteFile(g_out, @b[0], 1, @w, null); } + +main :: () { + g_out = GetStdHandle(0xFFFFFFF5); + good := run(); + gg := good & 0xffff; gx := (good >> 16) & 0xffff; + emit_num(gg); emit_b(32); emit_num(gx); emit_b(32); + pass := 0; + if gg == 0 { if gx == 0 { pass = 1; } } + if pass == 1 { emit_b(80); } else { emit_b(70); } // 'P' / 'F' + emit_b(10); + ExitProcess(0); +} diff --git a/examples/expected/1810-concurrency-fiber-switch-win64.build b/examples/expected/1810-concurrency-fiber-switch-win64.build new file mode 100644 index 00000000..196aeb99 --- /dev/null +++ b/examples/expected/1810-concurrency-fiber-switch-win64.build @@ -0,0 +1 @@ +{ "target": "x86_64-windows-gnu" } diff --git a/examples/expected/1810-concurrency-fiber-switch-win64.exit b/examples/expected/1810-concurrency-fiber-switch-win64.exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/examples/expected/1810-concurrency-fiber-switch-win64.exit @@ -0,0 +1 @@ +0 diff --git a/examples/expected/1810-concurrency-fiber-switch-win64.ir b/examples/expected/1810-concurrency-fiber-switch-win64.ir new file mode 100644 index 00000000..81d7fe86 --- /dev/null +++ b/examples/expected/1810-concurrency-fiber-switch-win64.ir @@ -0,0 +1,326 @@ + +module asm ".global fib_tramp" +module asm "fib_tramp:" +module asm " movq %rbx, %rcx" +module asm " subq $32, %rsp" +module asm " call fib_body" +module asm " ud2" + +@g_out = internal global ptr null + +; Function Attrs: nounwind +declare ptr @GetStdHandle(i32) #0 + +; Function Attrs: nounwind +declare i32 @WriteFile(ptr, ptr, i32, ptr, ptr) #0 + +; Function Attrs: nounwind +declare void @ExitProcess(i32) #0 + +; Function Attrs: naked noinline nounwind +define void @swap_good(ptr %0, ptr %1) #1 { +entry: + call void asm sideeffect " movq %rbx, 0(%rcx)\0A movq %rbp, 8(%rcx)\0A movq %rdi, 16(%rcx)\0A movq %rsi, 24(%rcx)\0A movq %rN, 32(%rcx)\0A movq %rN, 40(%rcx)\0A movq %rN, 48(%rcx)\0A movq %rN, 56(%rcx)\0A movq %rsp, 64(%rcx)\0A movups %xmmN, 80(%rcx)\0A movups %xmmN, 96(%rcx)\0A movups %xmmN, 112(%rcx)\0A movups %xmmN, 128(%rcx)\0A movups %xmmN, 144(%rcx)\0A movups %xmmN, 160(%rcx)\0A movups %xmmN, 176(%rcx)\0A movups %xmmN, 192(%rcx)\0A movups %xmmN, 208(%rcx)\0A movups %xmmN, 224(%rcx)\0A movq 0(%rdx), %rbx\0A movq 8(%rdx), %rbp\0A movq 16(%rdx), %rdi\0A movq 24(%rdx), %rsi\0A movq 32(%rdx), %rN\0A movq 40(%rdx), %rN\0A movq 48(%rdx), %rN\0A movq 56(%rdx), %rN\0A movups 80(%rdx), %xmmN\0A movups 96(%rdx), %xmmN\0A movups 112(%rdx), %xmmN\0A movups 128(%rdx), %xmmN\0A movups 144(%rdx), %xmmN\0A movups 160(%rdx), %xmmN\0A movups 176(%rdx), %xmmN\0A movups 192(%rdx), %xmmN\0A movups 208(%rdx), %xmmN\0A movups 224(%rdx), %xmmN\0A movq 64(%rdx), %rsp\0A ret\0A", ""() + unreachable +} + +; Function Attrs: naked noinline nounwind +define i64 @scribble_verify(ptr %0, ptr %1, i64 %2) #1 { +entry: + call void asm sideeffect " subq $$264, %rsp\0A movq %rN, 32(%rsp)\0A movq %rbx, 40(%rsp)\0A movq %rbp, 48(%rsp)\0A movq %rdi, 56(%rsp)\0A movq %rsi, 64(%rsp)\0A movq %rN, 72(%rsp)\0A movq %rN, 80(%rsp)\0A movq %rN, 88(%rsp)\0A movq %rN, 96(%rsp)\0A movups %xmmN, 104(%rsp)\0A movups %xmmN, 120(%rsp)\0A movups %xmmN, 136(%rsp)\0A movups %xmmN, 152(%rsp)\0A movups %xmmN, 168(%rsp)\0A movups %xmmN, 184(%rsp)\0A movups %xmmN, 200(%rsp)\0A movups %xmmN, 216(%rsp)\0A movups %xmmN, 232(%rsp)\0A movups %xmmN, 248(%rsp)\0A leaq 1(%rN), %rbx\0A leaq 2(%rN), %rbp\0A leaq 3(%rN), %rdi\0A leaq 4(%rN), %rsi\0A leaq 5(%rN), %rN\0A leaq 6(%rN), %rN\0A leaq 7(%rN), %rN\0A leaq 8(%rN), %rN\0A leaq 9(%rN), %rax\0A movq %rax, %xmmN\0A leaq 10(%rN), %rax\0A movq %rax, %xmmN\0A leaq 11(%rN), %rax\0A movq %rax, %xmmN\0A leaq 12(%rN), %rax\0A movq %rax, %xmmN\0A leaq 13(%rN), %rax\0A movq %rax, %xmmN\0A leaq 14(%rN), %rax\0A movq %rax, %xmmN\0A leaq 15(%rN), %rax\0A movq %rax, %xmmN\0A leaq 16(%rN), %rax\0A movq %rax, %xmmN\0A leaq 17(%rN), %rax\0A movq %rax, %xmmN\0A leaq 18(%rN), %rax\0A movq %rax, %xmmN\0A call swap_good\0A movq 32(%rsp), %rN\0A xorq %rN, %rN\0A xorq %rN, %rN\0A leaq 1(%rN), %rax\0A cmpq %rax, %rbx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 2(%rN), %rax\0A cmpq %rax, %rbp\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 3(%rN), %rax\0A cmpq %rax, %rdi\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 4(%rN), %rax\0A cmpq %rax, %rsi\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 5(%rN), %rax\0A cmpq %rax, %rN\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 6(%rN), %rax\0A cmpq %rax, %rN\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 7(%rN), %rax\0A cmpq %rax, %rN\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 8(%rN), %rax\0A cmpq %rax, %rN\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 9(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 10(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 11(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 12(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 13(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 14(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 15(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 16(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 17(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 18(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A movq %rN, %rax\0A shlq $$16, %rax\0A orq %rN, %rax\0A movq 40(%rsp), %rbx\0A movq 48(%rsp), %rbp\0A movq 56(%rsp), %rdi\0A movq 64(%rsp), %rsi\0A movq 72(%rsp), %rN\0A movq 80(%rsp), %rN\0A movq 88(%rsp), %rN\0A movq 96(%rsp), %rN\0A movups 104(%rsp), %xmmN\0A movups 120(%rsp), %xmmN\0A movups 136(%rsp), %xmmN\0A movups 152(%rsp), %xmmN\0A movups 168(%rsp), %xmmN\0A movups 184(%rsp), %xmmN\0A movups 200(%rsp), %xmmN\0A movups 216(%rsp), %xmmN\0A movups 232(%rsp), %xmmN\0A movups 248(%rsp), %xmmN\0A addq $$264, %rsp\0A ret\0A", ""() + unreachable +} + +; Function Attrs: nounwind +declare void @fib_tramp() #0 + +; Function Attrs: nounwind +define void @fib_body(ptr %0) #0 { +entry: + %alloca = alloca ptr, align 8 + store ptr %0, ptr %alloca, align 8 + %load = load ptr, ptr %alloca, align 8 + %gep = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %load, i32 0, i32 0 + %loadN = load ptr, ptr %alloca, align 8 + %loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, align 8 + %sg = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 1 + %loadN = load ptr, ptr %alloca, align 8 + %loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, align 8 + %sgN = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 3 + %call = call i64 @scribble_verify(ptr %gep, ptr %sg, i64 %sgN) + %allocaN = alloca i64, align 8 + store i64 %call, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %and = and i64 %loadN, 65535 + %loadN = load ptr, ptr %alloca, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 4 + store i64 %and, ptr %gepN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %lshr = lshr i64 %loadN, 16 + %andN = and i64 %lshr, 65535 + %loadN = load ptr, ptr %alloca, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 5 + store i64 %andN, ptr %gepN, align 8 + %loadN = load ptr, ptr %alloca, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 0 + %loadN = load ptr, ptr %alloca, align 8 + %loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, align 8 + %sgN = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 2 + call void @swap_good(ptr %gepN, ptr %sgN) + ret void +} + +; Function Attrs: nounwind +define internal void @boot(ptr %0) #0 { +entry: + %alloca = alloca ptr, align 8 + store ptr %0, ptr %alloca, align 8 + %allocaN = alloca ptr, align 8 + %call = call ptr @VirtualAlloc(ptr null, i64 131072, i32 12288, i32 4) + store ptr %call, ptr %allocaN, align 8 + %allocaN = alloca i64, align 8 + %load = load ptr, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + %add = add i64 %pti, 131072 + store i64 %add, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %urem = urem i64 %loadN, 16 + %sub = sub i64 %loadN, %urem + store i64 %sub, ptr %allocaN, align 8 + %allocaN = alloca ptr, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %subN = sub i64 %loadN, 8 + %itp = inttoptr i64 %subN to ptr + store ptr %itp, ptr %allocaN, align 8 + %loadN = load ptr, ptr %allocaN, align 8 + store ptr @fib_tramp, ptr %loadN, align 8 + %allocaN = alloca i64, align 8 + store i64 0, ptr %allocaN, align 8 + br label %while.hdr.0 + +while.hdr.0: ; preds = %while.body.1, %entry + %loadN = load i64, ptr %allocaN, align 8 + %icmp = icmp slt i64 %loadN, 30 + br i1 %icmp, label %while.body.1, label %while.exit.2 + +while.body.1: ; preds = %while.hdr.0 + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load ptr, ptr %alloca, align 8 + %gep = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 0 + %gepN = getelementptr inbounds nuw { [30 x i64] }, ptr %gep, i32 0, i32 0 + %igp.ptr = getelementptr i64, ptr %gepN, i64 %loadN + store i64 0, ptr %igp.ptr, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + store i64 %addN, ptr %allocaN, align 8 + br label %while.hdr.0 + +while.exit.2: ; preds = %while.hdr.0 + %loadN = load ptr, ptr %alloca, align 8 + %ptiN = ptrtoint ptr %loadN to i64 + %loadN = load ptr, ptr %alloca, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 0 + %gepN = getelementptr inbounds nuw { [30 x i64] }, ptr %gepN, i32 0, i32 0 + %igp.ptr21 = getelementptr i64, ptr %gepN, i64 0 + store i64 %ptiN, ptr %igp.ptr21, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %subN = sub i64 %loadN, 8 + %loadN = load ptr, ptr %alloca, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 0 + %gepN = getelementptr inbounds nuw { [30 x i64] }, ptr %gepN, i32 0, i32 0 + %igp.ptr27 = getelementptr i64, ptr %gepN, i64 8 + store i64 %subN, ptr %igp.ptr27, align 8 + %loadN = load ptr, ptr %alloca, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 4 + store i64 0, ptr %gepN, align 8 + %loadN = load ptr, ptr %alloca, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 5 + store i64 0, ptr %gepN, align 8 + ret void +} + +; Function Attrs: nounwind +declare ptr @VirtualAlloc(ptr, i64, i32, i32) #0 + +; Function Attrs: nounwind +define internal i64 @run() #0 { +entry: + %alloca = alloca { [30 x i64] }, align 8 + store { [30 x i64] } undef, ptr %alloca, align 8 + %allocaN = alloca { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, align 8 + store { { [30 x i64] }, ptr, ptr, i64, i64, i64 } undef, ptr %allocaN, align 8 + %gep = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 3 + store i64 20480, ptr %gep, align 8 + %allocaN = alloca { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, align 8 + store { { [30 x i64] }, ptr, ptr, i64, i64, i64 } undef, ptr %allocaN, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 3 + store i64 24576, ptr %gepN, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 0 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 1 + store ptr %gepN, ptr %gepN, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 0 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 2 + store ptr %gepN, ptr %gepN, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 0 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 1 + store ptr %gepN, ptr %gepN, align 8 + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 2 + store ptr %alloca, ptr %gepN, align 8 + call void @boot(ptr %allocaN) + call void @boot(ptr %allocaN) + %gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 0 + call void @swap_good(ptr %alloca, ptr %gepN) + %load = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, align 8 + %sg = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %load, 5 + %loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, align 8 + %sgN = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 5 + %add = add i64 %sg, %sgN + %shl = shl i64 %add, 16 + %loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, align 8 + %sgN = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 4 + %loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, align 8 + %sgN = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 4 + %addN = add i64 %sgN, %sgN + %or = or i64 %shl, %addN + ret i64 %or +} + +; Function Attrs: nounwind +define internal void @emit_num(i64 %0) #0 { +entry: + %alloca = alloca i64, align 8 + %allocaN = alloca [1 x i8], align 1 + %allocaN = alloca i32, align 4 + %allocaN = alloca [20 x i8], align 1 + %allocaN = alloca i64, align 8 + %allocaN = alloca i64, align 8 + %allocaN = alloca i32, align 4 + store i64 %0, ptr %alloca, align 8 + %load = load i64, ptr %alloca, align 8 + %icmp = icmp eq i64 %load, 0 + br i1 %icmp, label %if.then.3, label %if.merge.4 + +if.then.3: ; preds = %entry + store [1 x i8] c"0", ptr %allocaN, align 1 + store i32 0, ptr %allocaN, align 4 + %gload = load ptr, ptr @g_out, align 8 + %igp.ptr = getelementptr i8, ptr %allocaN, i64 0 + %call = call i32 @WriteFile(ptr %gload, ptr %igp.ptr, i32 1, ptr %allocaN, ptr null) + ret void + +if.merge.4: ; preds = %entry + store i64 20, ptr %allocaN, align 8 + %loadN = load i64, ptr %alloca, align 8 + store i64 %loadN, ptr %allocaN, align 8 + br label %while.hdr.5 + +while.hdr.5: ; preds = %while.body.6, %if.merge.4 + %loadN = load i64, ptr %allocaN, align 8 + %icmpN = icmp ugt i64 %loadN, 0 + br i1 %icmpN, label %while.body.6, label %while.exit.7 + +while.body.6: ; preds = %while.hdr.5 + %loadN = load i64, ptr %allocaN, align 8 + %sub = sub i64 %loadN, 1 + store i64 %sub, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %urem = urem i64 %loadN, 10 + %add = add i64 48, %urem + %trunc = trunc i64 %add to i8 + %loadN = load i64, ptr %allocaN, align 8 + %igp.ptr12 = getelementptr i8, ptr %allocaN, i64 %loadN + store i8 %trunc, ptr %igp.ptr12, align 1 + %loadN = load i64, ptr %allocaN, align 8 + %udiv = udiv i64 %loadN, 10 + store i64 %udiv, ptr %allocaN, align 8 + br label %while.hdr.5 + +while.exit.7: ; preds = %while.hdr.5 + store i32 0, ptr %allocaN, align 4 + %gloadN = load ptr, ptr @g_out, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %igp.ptr17 = getelementptr i8, ptr %allocaN, i64 %loadN + %loadN = load i64, ptr %allocaN, align 8 + %subN = sub i64 20, %loadN + %truncN = trunc i64 %subN to i32 + %callN = call i32 @WriteFile(ptr %gloadN, ptr %igp.ptr17, i32 %truncN, ptr %allocaN, ptr null) + ret void +} + +; Function Attrs: nounwind +define internal void @emit_b(i8 %0) #0 { +entry: + %alloca = alloca i8, align 1 + store i8 %0, ptr %alloca, align 1 + %allocaN = alloca [1 x i8], align 1 + %load = load i8, ptr %alloca, align 1 + %igp.ptr = getelementptr i8, ptr %allocaN, i64 0 + store i8 %load, ptr %igp.ptr, align 1 + %allocaN = alloca i32, align 4 + store i32 0, ptr %allocaN, align 4 + %gload = load ptr, ptr @g_out, align 8 + %igp.ptr3 = getelementptr i8, ptr %allocaN, i64 0 + %call = call i32 @WriteFile(ptr %gload, ptr %igp.ptr3, i32 1, ptr %allocaN, ptr null) + ret void +} + +; Function Attrs: nounwind +define i32 @main() #0 { +entry: + %call = call ptr @GetStdHandle(i32 -11) + store ptr %call, ptr @g_out, align 8 + %callN = call i64 @run() + %alloca = alloca i64, align 8 + store i64 %callN, ptr %alloca, align 8 + %load = load i64, ptr %alloca, align 8 + %and = and i64 %load, 65535 + %allocaN = alloca i64, align 8 + store i64 %and, ptr %allocaN, align 8 + %loadN = load i64, ptr %alloca, align 8 + %lshr = lshr i64 %loadN, 16 + %andN = and i64 %lshr, 65535 + %allocaN = alloca i64, align 8 + store i64 %andN, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + call void @emit_num(i64 %loadN) + call void @emit_b(i8 32) + %loadN = load i64, ptr %allocaN, align 8 + call void @emit_num(i64 %loadN) + call void @emit_b(i8 32) + %allocaN = alloca i64, align 8 + store i64 0, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %icmp = icmp eq i64 %loadN, 0 + br i1 %icmp, label %if.then.8, label %if.merge.9 + +if.then.8: ; preds = %entry + %loadN = load i64, ptr %allocaN, align 8 + %icmpN = icmp eq i64 %loadN, 0 + br i1 %icmpN, label %if.then.10, label %if.merge.11 + +if.merge.9: ; preds = %if.merge.11, %entry + %loadN = load i64, ptr %allocaN, align 8 + %icmpN = icmp eq i64 %loadN, 1 + br i1 %icmpN, label %if.then.12, label %if.else.13 + +if.then.10: ; preds = %if.then.8 + store i64 1, ptr %allocaN, align 8 + br label %if.merge.11 + +if.merge.11: ; preds = %if.then.10, %if.then.8 + br label %if.merge.9 + +if.then.12: ; preds = %if.merge.9 + call void @emit_b(i8 80) + br label %if.merge.14 + +if.else.13: ; preds = %if.merge.9 + call void @emit_b(i8 70) + br label %if.merge.14 + +if.merge.14: ; preds = %if.else.13, %if.then.12 + call void @emit_b(i8 10) + call void @ExitProcess(i32 0) + ret i32 0 +} diff --git a/examples/expected/1810-concurrency-fiber-switch-win64.stderr b/examples/expected/1810-concurrency-fiber-switch-win64.stderr new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/examples/expected/1810-concurrency-fiber-switch-win64.stderr @@ -0,0 +1 @@ +