blob: 422ecf2a5e21513363e48d81a262c5cb9b719395 [file] [log] [blame] [edit]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals
; RUN: opt -S -passes='require<collector-metadata>,shadow-stack-gc-lowering' < %s | FileCheck %s
declare void @llvm.gcroot(ptr, ptr)
; A gc "shadow-stack" function with a single root: the pass should create a
; gc_frame alloca and gc_stackentry struct, push the frame onto the chain at
; entry, and pop it before every return.
;.
; CHECK: @type_tag = external constant i8
; CHECK: @llvm_gc_root_chain = linkonce global ptr null
; CHECK: @__gc_single_root = internal constant %gc_map.0 { %gc_map { i32 1, i32 0 }, [0 x ptr] zeroinitializer }
; CHECK: @__gc_two_roots = internal constant %gc_map.0.0 { %gc_map { i32 2, i32 0 }, [0 x ptr] zeroinitializer }
; CHECK: @__gc_root_with_metadata = internal constant %gc_map.1 { %gc_map { i32 1, i32 1 }, [1 x ptr] [ptr @type_tag] }
; CHECK: @__gc_mixed_metadata = internal constant %gc_map.1.1 { %gc_map { i32 2, i32 1 }, [1 x ptr] [ptr @type_tag] }
; CHECK: @__gc_with_invoke = internal constant %gc_map.0.2 { %gc_map { i32 1, i32 0 }, [0 x ptr] zeroinitializer }
;.
define void @single_root(ptr %obj) gc "shadow-stack" {
; CHECK-LABEL: @single_root(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[GC_FRAME:%.*]] = alloca [[GC_STACKENTRY_SINGLE_ROOT:%.*]], align 8
; CHECK-NEXT: [[GC_CURRHEAD:%.*]] = load ptr, ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: [[GC_FRAME_MAP:%.*]] = getelementptr [[GC_STACKENTRY_SINGLE_ROOT]], ptr [[GC_FRAME]], i32 0, i32 0, i32 1
; CHECK-NEXT: store ptr @__gc_single_root, ptr [[GC_FRAME_MAP]], align 8
; CHECK-NEXT: [[ROOT:%.*]] = getelementptr [[GC_STACKENTRY_SINGLE_ROOT]], ptr [[GC_FRAME]], i32 0, i32 1
; CHECK-NEXT: [[GC_FRAME_NEXT:%.*]] = getelementptr [[GC_STACKENTRY_SINGLE_ROOT]], ptr [[GC_FRAME]], i32 0, i32 0, i32 0
; CHECK-NEXT: [[GC_NEWHEAD:%.*]] = getelementptr [[GC_STACKENTRY_SINGLE_ROOT]], ptr [[GC_FRAME]], i32 0, i32 0
; CHECK-NEXT: store ptr [[GC_CURRHEAD]], ptr [[GC_FRAME_NEXT]], align 8
; CHECK-NEXT: store ptr [[GC_NEWHEAD]], ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: store ptr [[OBJ:%.*]], ptr [[ROOT]], align 8
; CHECK-NEXT: [[GC_FRAME_NEXT1:%.*]] = getelementptr [[GC_STACKENTRY_SINGLE_ROOT]], ptr [[GC_FRAME]], i32 0, i32 0, i32 0
; CHECK-NEXT: [[GC_SAVEDHEAD:%.*]] = load ptr, ptr [[GC_FRAME_NEXT1]], align 8
; CHECK-NEXT: store ptr [[GC_SAVEDHEAD]], ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: ret void
;
entry:
%root = alloca ptr
call void @llvm.gcroot(ptr %root, ptr null)
store ptr %obj, ptr %root
ret void
}
; Two roots with no metadata: the frame map should have NumRoots=2, NumMeta=0
; and the concrete stack entry should have two root slots.
define void @two_roots(ptr %a, ptr %b) gc "shadow-stack" {
; CHECK-LABEL: @two_roots(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[GC_FRAME:%.*]] = alloca [[GC_STACKENTRY_TWO_ROOTS:%.*]], align 8
; CHECK-NEXT: [[GC_CURRHEAD:%.*]] = load ptr, ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: [[GC_FRAME_MAP:%.*]] = getelementptr [[GC_STACKENTRY_TWO_ROOTS]], ptr [[GC_FRAME]], i32 0, i32 0, i32 1
; CHECK-NEXT: store ptr @__gc_two_roots, ptr [[GC_FRAME_MAP]], align 8
; CHECK-NEXT: [[ROOTA:%.*]] = getelementptr [[GC_STACKENTRY_TWO_ROOTS]], ptr [[GC_FRAME]], i32 0, i32 1
; CHECK-NEXT: [[ROOTB:%.*]] = getelementptr [[GC_STACKENTRY_TWO_ROOTS]], ptr [[GC_FRAME]], i32 0, i32 2
; CHECK-NEXT: [[GC_FRAME_NEXT:%.*]] = getelementptr [[GC_STACKENTRY_TWO_ROOTS]], ptr [[GC_FRAME]], i32 0, i32 0, i32 0
; CHECK-NEXT: [[GC_NEWHEAD:%.*]] = getelementptr [[GC_STACKENTRY_TWO_ROOTS]], ptr [[GC_FRAME]], i32 0, i32 0
; CHECK-NEXT: store ptr [[GC_CURRHEAD]], ptr [[GC_FRAME_NEXT]], align 8
; CHECK-NEXT: store ptr [[GC_NEWHEAD]], ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: store ptr [[A:%.*]], ptr [[ROOTA]], align 8
; CHECK-NEXT: store ptr [[B:%.*]], ptr [[ROOTB]], align 8
; CHECK-NEXT: [[GC_FRAME_NEXT1:%.*]] = getelementptr [[GC_STACKENTRY_TWO_ROOTS]], ptr [[GC_FRAME]], i32 0, i32 0, i32 0
; CHECK-NEXT: [[GC_SAVEDHEAD:%.*]] = load ptr, ptr [[GC_FRAME_NEXT1]], align 8
; CHECK-NEXT: store ptr [[GC_SAVEDHEAD]], ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: ret void
;
entry:
%rootA = alloca ptr
%rootB = alloca ptr
call void @llvm.gcroot(ptr %rootA, ptr null)
call void @llvm.gcroot(ptr %rootB, ptr null)
store ptr %a, ptr %rootA
store ptr %b, ptr %rootB
ret void
}
; Root with a non-null metadata argument: NumMeta should be 1, and the
; gc_map struct should include the trailing metadata pointer array.
; Roots with metadata are sorted before null-metadata roots.
@type_tag = external constant i8
define void @root_with_metadata(ptr %obj) gc "shadow-stack" {
; CHECK-LABEL: @root_with_metadata(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[GC_FRAME:%.*]] = alloca [[GC_STACKENTRY_ROOT_WITH_METADATA:%.*]], align 8
; CHECK-NEXT: [[GC_CURRHEAD:%.*]] = load ptr, ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: [[GC_FRAME_MAP:%.*]] = getelementptr [[GC_STACKENTRY_ROOT_WITH_METADATA]], ptr [[GC_FRAME]], i32 0, i32 0, i32 1
; CHECK-NEXT: store ptr @__gc_root_with_metadata, ptr [[GC_FRAME_MAP]], align 8
; CHECK-NEXT: [[ROOT:%.*]] = getelementptr [[GC_STACKENTRY_ROOT_WITH_METADATA]], ptr [[GC_FRAME]], i32 0, i32 1
; CHECK-NEXT: [[GC_FRAME_NEXT:%.*]] = getelementptr [[GC_STACKENTRY_ROOT_WITH_METADATA]], ptr [[GC_FRAME]], i32 0, i32 0, i32 0
; CHECK-NEXT: [[GC_NEWHEAD:%.*]] = getelementptr [[GC_STACKENTRY_ROOT_WITH_METADATA]], ptr [[GC_FRAME]], i32 0, i32 0
; CHECK-NEXT: store ptr [[GC_CURRHEAD]], ptr [[GC_FRAME_NEXT]], align 8
; CHECK-NEXT: store ptr [[GC_NEWHEAD]], ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: store ptr [[OBJ:%.*]], ptr [[ROOT]], align 8
; CHECK-NEXT: [[GC_FRAME_NEXT1:%.*]] = getelementptr [[GC_STACKENTRY_ROOT_WITH_METADATA]], ptr [[GC_FRAME]], i32 0, i32 0, i32 0
; CHECK-NEXT: [[GC_SAVEDHEAD:%.*]] = load ptr, ptr [[GC_FRAME_NEXT1]], align 8
; CHECK-NEXT: store ptr [[GC_SAVEDHEAD]], ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: ret void
;
entry:
%root = alloca ptr
call void @llvm.gcroot(ptr %root, ptr @type_tag)
store ptr %obj, ptr %root
ret void
}
; Mixed: one root with metadata, one without. The metadata root should be
; placed first in the frame (per CollectRoots ordering), NumMeta=1, NumRoots=2.
define void @mixed_metadata(ptr %a, ptr %b) gc "shadow-stack" {
; CHECK-LABEL: @mixed_metadata(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[GC_FRAME:%.*]] = alloca [[GC_STACKENTRY_MIXED_METADATA:%.*]], align 8
; CHECK-NEXT: [[GC_CURRHEAD:%.*]] = load ptr, ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: [[GC_FRAME_MAP:%.*]] = getelementptr [[GC_STACKENTRY_MIXED_METADATA]], ptr [[GC_FRAME]], i32 0, i32 0, i32 1
; CHECK-NEXT: store ptr @__gc_mixed_metadata, ptr [[GC_FRAME_MAP]], align 8
; CHECK-NEXT: [[ROOTB:%.*]] = getelementptr [[GC_STACKENTRY_MIXED_METADATA]], ptr [[GC_FRAME]], i32 0, i32 1
; CHECK-NEXT: [[ROOTA:%.*]] = getelementptr [[GC_STACKENTRY_MIXED_METADATA]], ptr [[GC_FRAME]], i32 0, i32 2
; CHECK-NEXT: [[GC_FRAME_NEXT:%.*]] = getelementptr [[GC_STACKENTRY_MIXED_METADATA]], ptr [[GC_FRAME]], i32 0, i32 0, i32 0
; CHECK-NEXT: [[GC_NEWHEAD:%.*]] = getelementptr [[GC_STACKENTRY_MIXED_METADATA]], ptr [[GC_FRAME]], i32 0, i32 0
; CHECK-NEXT: store ptr [[GC_CURRHEAD]], ptr [[GC_FRAME_NEXT]], align 8
; CHECK-NEXT: store ptr [[GC_NEWHEAD]], ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: store ptr [[A:%.*]], ptr [[ROOTA]], align 8
; CHECK-NEXT: store ptr [[B:%.*]], ptr [[ROOTB]], align 8
; CHECK-NEXT: [[GC_FRAME_NEXT1:%.*]] = getelementptr [[GC_STACKENTRY_MIXED_METADATA]], ptr [[GC_FRAME]], i32 0, i32 0, i32 0
; CHECK-NEXT: [[GC_SAVEDHEAD:%.*]] = load ptr, ptr [[GC_FRAME_NEXT1]], align 8
; CHECK-NEXT: store ptr [[GC_SAVEDHEAD]], ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: ret void
;
entry:
%rootA = alloca ptr
%rootB = alloca ptr
call void @llvm.gcroot(ptr %rootA, ptr null)
call void @llvm.gcroot(ptr %rootB, ptr @type_tag)
store ptr %a, ptr %rootA
store ptr %b, ptr %rootB
ret void
}
; A gc "shadow-stack" function with no gcroot calls: the pass must leave the
; function unchanged (no gc_frame alloca, no push/pop of the shadow stack).
define void @no_roots() gc "shadow-stack" {
; CHECK-LABEL: @no_roots(
; CHECK-NEXT: entry:
; CHECK-NEXT: ret void
;
entry:
ret void
}
; A function with an invoke: the EscapeEnumerator must insert a shadow stack
; pop on the unwind path as well as on the normal return path.
declare void @may_throw()
declare ptr @__gxx_personality_v0(...)
define void @with_invoke(ptr %obj) gc "shadow-stack" personality ptr @__gxx_personality_v0 {
; CHECK-LABEL: @with_invoke(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[GC_FRAME:%.*]] = alloca [[GC_STACKENTRY_WITH_INVOKE:%.*]], align 8
; CHECK-NEXT: [[GC_CURRHEAD:%.*]] = load ptr, ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: [[GC_FRAME_MAP:%.*]] = getelementptr [[GC_STACKENTRY_WITH_INVOKE]], ptr [[GC_FRAME]], i32 0, i32 0, i32 1
; CHECK-NEXT: store ptr @__gc_with_invoke, ptr [[GC_FRAME_MAP]], align 8
; CHECK-NEXT: [[ROOT:%.*]] = getelementptr [[GC_STACKENTRY_WITH_INVOKE]], ptr [[GC_FRAME]], i32 0, i32 1
; CHECK-NEXT: [[GC_FRAME_NEXT:%.*]] = getelementptr [[GC_STACKENTRY_WITH_INVOKE]], ptr [[GC_FRAME]], i32 0, i32 0, i32 0
; CHECK-NEXT: [[GC_NEWHEAD:%.*]] = getelementptr [[GC_STACKENTRY_WITH_INVOKE]], ptr [[GC_FRAME]], i32 0, i32 0
; CHECK-NEXT: store ptr [[GC_CURRHEAD]], ptr [[GC_FRAME_NEXT]], align 8
; CHECK-NEXT: store ptr [[GC_NEWHEAD]], ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: store ptr [[OBJ:%.*]], ptr [[ROOT]], align 8
; CHECK-NEXT: invoke void @may_throw()
; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[UNWIND:%.*]]
; CHECK: normal:
; CHECK-NEXT: [[GC_FRAME_NEXT1:%.*]] = getelementptr [[GC_STACKENTRY_WITH_INVOKE]], ptr [[GC_FRAME]], i32 0, i32 0, i32 0
; CHECK-NEXT: [[GC_SAVEDHEAD:%.*]] = load ptr, ptr [[GC_FRAME_NEXT1]], align 8
; CHECK-NEXT: store ptr [[GC_SAVEDHEAD]], ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: ret void
; CHECK: unwind:
; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: cleanup
; CHECK-NEXT: [[GC_FRAME_NEXT2:%.*]] = getelementptr [[GC_STACKENTRY_WITH_INVOKE]], ptr [[GC_FRAME]], i32 0, i32 0, i32 0
; CHECK-NEXT: [[GC_SAVEDHEAD3:%.*]] = load ptr, ptr [[GC_FRAME_NEXT2]], align 8
; CHECK-NEXT: store ptr [[GC_SAVEDHEAD3]], ptr @llvm_gc_root_chain, align 8
; CHECK-NEXT: resume { ptr, i32 } [[LP]]
;
entry:
%root = alloca ptr
call void @llvm.gcroot(ptr %root, ptr null)
store ptr %obj, ptr %root
invoke void @may_throw() to label %normal unwind label %unwind
normal:
ret void
unwind:
%lp = landingpad { ptr, i32 } cleanup
resume { ptr, i32 } %lp
}
;.
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nounwind }
;.