67 lines · plain
1; Tests that CoroSplit does not place a dynamically sized alloca (a VLA) on the2; coroutine frame when it does not actually live across a suspend point, even if3; its address escapes to a call.4;5; This is the glibc _nl_load_locale shape on the wasm32 coro arm: a VLA path6; buffer is filled and handed by pointer to a (non-capturing) syscall wrapper,7; all before the first suspend, and is never used afterwards. The generic8; "escaped => must live on frame" heuristic used to collect such an alloca onto9; the frame; FrameTypeBuilder::addFieldForAllocas then sorted the frame allocas10; by AllocaInst::getAllocationSize(), which is std::nullopt for a VLA, and (with11; assertions disabled, as in the shipped toolchain) dereferenced the empty12; optional. That made the sort comparator non-deterministic and corrupted the13; frame layout -> SIGSEGV while splitting the coroutine. With assertions it hit14; "Coroutines cannot handle non static allocas yet".15;16; A VLA whose uses do not cross a suspend does not belong on the frame at all --17; it stays a normal stack alloca in the ramp function -- so the coroutine now18; splits cleanly. reuse-storage selects OptimizeFrame, matching clang at -O2.19;20; RUN: opt < %s -passes='cgscc(coro-split<reuse-storage>)' -S | FileCheck %s21 22define ptr @f(i32 %n) presplitcoroutine {23entry:24 %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)25 %size = call i32 @llvm.coro.size.i32()26 %alloc = call ptr @malloc(i32 %size)27 %hdl = call ptr @llvm.coro.begin(token %id, ptr %alloc)28 ; A dynamically sized alloca whose address escapes (no nocapture) to a call,29 ; all before the suspend, and which is dead afterwards.30 %vla = alloca i8, i32 %n, align 1631 call void @use(ptr %vla)32 %sp = call i8 @llvm.coro.suspend(token none, i1 false)33 switch i8 %sp, label %suspend [i8 0, label %resume34 i8 1, label %cleanup]35resume:36 br label %cleanup37 38cleanup:39 %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)40 call void @free(ptr %mem)41 br label %suspend42 43suspend:44 call void @llvm.coro.end(ptr %hdl, i1 0, token none)45 ret ptr %hdl46}47 48; The coroutine splits cleanly (no crash). The VLA stays a normal dynamic alloca49; in the ramp function rather than being materialized as a frame field.50 51; CHECK-LABEL: define ptr @f(52; CHECK: %vla = alloca i8, i32 %n53; CHECK: call void @use(ptr %vla)54 55; The resume clone exists (i.e. the coroutine was actually split).56; CHECK-LABEL: define internal fastcc void @f.resume(57 58declare token @llvm.coro.id(i32, ptr, ptr, ptr)59declare i32 @llvm.coro.size.i32()60declare ptr @llvm.coro.begin(token, ptr)61declare i8 @llvm.coro.suspend(token, i1)62declare ptr @llvm.coro.free(token, ptr)63declare void @llvm.coro.end(ptr, i1, token)64declare ptr @malloc(i32)65declare void @free(ptr)66declare void @use(ptr)67