mirror of
https://github.com/RPCSX/llvm.git
synced 2024-12-14 07:31:47 +00:00
a1184492ba
Summary: If heap allocation of a coroutine is elided, we need to make sure that we will update an address stored in the coroutine frame from f.destroy to f.cleanup. Before this change, CoroSplit synthesized these stores after coro.begin: ``` store void (%f.Frame*)* @f.resume, void (%f.Frame*)** %resume.addr store void (%f.Frame*)* @f.destroy, void (%f.Frame*)** %destroy.addr ``` In those cases where we did heap elision, but were not able to devirtualize all indirect calls, destroy call will attempt to "free" the coroutine frame stored on the stack. Oops. Now we use select to put an appropriate coroutine subfunction in the destroy slot. As bellow: ``` store void (%f.Frame*)* @f.resume, void (%f.Frame*)** %resume.addr %0 = select i1 %need.alloc, void (%f.Frame*)* @f.destroy, void (%f.Frame*)* @f.cleanup store void (%f.Frame*)* %0, void (%f.Frame*)** %destroy.addr ``` Reviewers: majnemer Subscribers: mehdi_amini, llvm-commits Differential Revision: https://reviews.llvm.org/D25377 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@283625 91177308-0d34-0410-b5e6-96231b3b80d8
80 lines
2.3 KiB
LLVM
80 lines
2.3 KiB
LLVM
; Tests that coro-split pass splits the coroutine into f, f.resume and f.destroy
|
|
; RUN: opt < %s -coro-split -S | FileCheck %s
|
|
|
|
define i8* @f() "coroutine.presplit"="1" {
|
|
entry:
|
|
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
|
|
%need.alloc = call i1 @llvm.coro.alloc(token %id)
|
|
br i1 %need.alloc, label %dyn.alloc, label %begin
|
|
|
|
dyn.alloc:
|
|
%size = call i32 @llvm.coro.size.i32()
|
|
%alloc = call i8* @malloc(i32 %size)
|
|
br label %begin
|
|
|
|
begin:
|
|
%phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
|
|
%hdl = call i8* @llvm.coro.begin(token %id, i8* %phi)
|
|
call void @print(i32 0)
|
|
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
|
|
switch i8 %0, label %suspend [i8 0, label %resume
|
|
i8 1, label %cleanup]
|
|
resume:
|
|
call void @print(i32 1)
|
|
br label %cleanup
|
|
|
|
cleanup:
|
|
%mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
|
|
call void @free(i8* %mem)
|
|
br label %suspend
|
|
suspend:
|
|
call void @llvm.coro.end(i8* %hdl, i1 0)
|
|
ret i8* %hdl
|
|
}
|
|
|
|
; CHECK-LABEL: @f(
|
|
; CHECK: call i8* @malloc
|
|
; CHECK: @llvm.coro.begin(token %id, i8* %phi)
|
|
; CHECK: store void (%f.Frame*)* @f.resume, void (%f.Frame*)** %resume.addr
|
|
; CHECK: %[[SEL:.+]] = select i1 %need.alloc, void (%f.Frame*)* @f.destroy, void (%f.Frame*)* @f.cleanup
|
|
; CHECK: store void (%f.Frame*)* %[[SEL]], void (%f.Frame*)** %destroy.addr
|
|
; CHECK: call void @print(i32 0)
|
|
; CHECK-NOT: call void @print(i32 1)
|
|
; CHECK-NOT: call void @free(
|
|
; CHECK: ret i8* %hdl
|
|
|
|
; CHECK-LABEL: @f.resume(
|
|
; CHECK-NOT: call i8* @malloc
|
|
; CHECK-NOT: call void @print(i32 0)
|
|
; CHECK: call void @print(i32 1)
|
|
; CHECK-NOT: call void @print(i32 0)
|
|
; CHECK: call void @free(
|
|
; CHECK: ret void
|
|
|
|
; CHECK-LABEL: @f.destroy(
|
|
; CHECK-NOT: call i8* @malloc
|
|
; CHECK-NOT: call void @print(
|
|
; CHECK: call void @free(
|
|
; CHECK: ret void
|
|
|
|
; CHECK-LABEL: @f.cleanup(
|
|
; CHECK-NOT: call i8* @malloc
|
|
; CHECK-NOT: call void @print(
|
|
; CHECK-NOT: call void @free(
|
|
; CHECK: ret void
|
|
|
|
declare i8* @llvm.coro.free(token, i8*)
|
|
declare i32 @llvm.coro.size.i32()
|
|
declare i8 @llvm.coro.suspend(token, i1)
|
|
declare void @llvm.coro.resume(i8*)
|
|
declare void @llvm.coro.destroy(i8*)
|
|
|
|
declare token @llvm.coro.id(i32, i8*, i8*, i8*)
|
|
declare i1 @llvm.coro.alloc(token)
|
|
declare i8* @llvm.coro.begin(token, i8*)
|
|
declare void @llvm.coro.end(i8*, i1)
|
|
|
|
declare noalias i8* @malloc(i32)
|
|
declare void @print(i32)
|
|
declare void @free(i8*)
|