// RUN: %target-run-simple-swift(-parse-sil -Xfrontend -enable-pack-metadata-stack-promotion=true) // RUN: %target-swift-frontend -parse-sil -enable-pack-metadata-stack-promotion=true -emit-ir -primary-file %s | %IRGenFileCheck %s // REQUIRES: executable_test // REQUIRES: CPU=arm64 // Allocate metadata packs on the stack in a loop. If these packs weren't // deallocated, the stack would be exhausted. import Builtin import Swift struct S {} sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %looper = function_ref @looper : $@convention(thin) () -> () apply %looper() : $@convention(thin) () -> () // If the stack were exhausted while looping, the apply of %looper would crash // and execution would never reach this point. %out_literal = integer_literal $Builtin.Int32, 0 %out = struct $Int32 (%out_literal : $Builtin.Int32) return %out : $Int32 } sil @callee : $@convention(thin) () -> () { %retval = tuple () return %retval : $() } // CHECK-LABEL: define {{.*}}@looper( // CHECK-SAME: [[INT]] %0, [[INT]] %1, ptr %"each T_1", ptr %"each T_2") {{.*}} { // CHECK: [[ENTRY:entry]]: // CHECK: br label %[[HEADER:[^,]+]] // CHECK: [[HEADER]]: // CHECK: [[PREVIOUS:%[^,]+]] = phi i64 [ 10000000, %[[ENTRY]] ], [ [[REMAINING:%[^,]+]], %{{[^,]+}} ] // CHECK: [[COMBINED_PACK_SIZE:%[^,]+]] = add [[INT]] %0, %1 // CHECK: [[STACK_BEFORE_FIRST_ALLOCA:%[^,]+]] = call ptr @llvm.stacksave.p0() // CHECK: [[FIRST_ALLOCA_METADATA_PACK:%[^,]+]] = alloca ptr, [[INT]] [[COMBINED_PACK_SIZE]] // CHECK: call swiftcc void @callee([[INT]] [[COMBINED_PACK_SIZE]], ptr [[FIRST_ALLOCA_METADATA_PACK]]) // CHECK: [[COMBINED_PACK_SIZE_2:%[^,]+]] = add [[INT]] %1, %0 // CHECK: [[STACK_BEFORE_SECOND_ALLOCA:%[^,]+]] = call ptr @llvm.stacksave.p0() // CHECK: [[SECOND_ALLOCA_METADATA_PACK:%[^,]+]] = alloca ptr, [[INT]] [[COMBINED_PACK_SIZE_2]] // CHECK: call swiftcc void @callee([[INT]] [[COMBINED_PACK_SIZE_2]], ptr [[SECOND_ALLOCA_METADATA_PACK]]) // CHECK: [[REMAINING_AND_OVERFLOW:%[^,]+]] = call { i64, i1 } @llvm.ssub.with.overflow.i64(i64 [[PREVIOUS]], i64 1) // CHECK: [[REMAINING]] = extractvalue { i64, i1 } [[REMAINING_AND_OVERFLOW]], 0 // CHECK: [[IS_ZERO:%[^,]+]] = icmp eq i64 [[REMAINING]], 0 // CHECK: br i1 [[IS_ZERO]], label %[[EXIT:[^,]+]], label %[[BACKEDGE:[^,]+]] // CHECK: [[BACKEDGE]]: // CHECK: call void @llvm.stackrestore.p0(ptr [[STACK_BEFORE_SECOND_ALLOCA]]) // CHECK: call void @llvm.stackrestore.p0(ptr [[STACK_BEFORE_FIRST_ALLOCA]]) // CHECK: br label %[[HEADER]] // CHECK: [[EXIT]]: // CHECK: [[STACK_BEFORE_SECOND_ALLOCAlcssa:%.*]] = phi ptr [ [[STACK_BEFORE_SECOND_ALLOCA]], %{{.*}} ] // CHECK: [[STACK_BEFORE_FIRST_ALLOCAlcssa:%.*]] = phi ptr [ [[STACK_BEFORE_FIRST_ALLOCA]], %{{.*}} ] // CHECK: call void @llvm.stackrestore.p0(ptr [[STACK_BEFORE_SECOND_ALLOCAlcssa]]) // CHECK: call void @llvm.stackrestore.p0(ptr [[STACK_BEFORE_FIRST_ALLOCAlcssa]]) // CHECK: ret void // CHECK: } sil @looper : $@convention(thin) () -> () { entry: %callee = function_ref @callee : $@convention(thin) () -> () %initial = integer_literal $Builtin.Int64, 10000000 br header(%initial : $Builtin.Int64) header(%previous : $Builtin.Int64): apply %callee() : $@convention(thin) () -> () apply %callee() : $@convention(thin) () -> () %offset = integer_literal $Builtin.Int64, 1 %flag = integer_literal $Builtin.Int1, -1 %remainingAndOverflow = builtin "ssub_with_overflow_Int64"(%previous : $Builtin.Int64, %offset : $Builtin.Int64, %flag : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) %remaining = tuple_extract %remainingAndOverflow : $(Builtin.Int64, Builtin.Int1), 0 %zero = integer_literal $Builtin.Int64, 0 %isZero = builtin "cmp_eq_Int64"(%remaining : $Builtin.Int64, %zero : $Builtin.Int64) : $Builtin.Int1 cond_br %isZero, exit, backedge backedge: br header(%remaining : $Builtin.Int64) exit: %retval = tuple () return %retval : $() }