; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals ; RUN: opt -S -passes='require,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 } ;.