// RUN: %target-swift-frontend -disable-type-layout %s -gnone -emit-ir -I %S/Inputs | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize -DINT=i%target-ptrsize --check-prefix=CHECK-%target-cpu import Builtin sil_stage canonical enum Either { case Left(T) case Right(U) } enum EitherOr { case Left(T) case Middle case Center case Right(U) } class C {} sil_vtable C {} // -- The runtime doesn't track spare bits, so fixed instances of the dynamic // type can't use them. // CHECK-64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i64, i8 } @fixed_instances_dont_use_spare_bits(i64 %0, i8 %1) // CHECK-32-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i32, i8 } @fixed_instances_dont_use_spare_bits(i32 %0, i8 %1) sil @fixed_instances_dont_use_spare_bits : $@convention(thin) (@owned Either) -> @owned Either { entry(%e : $Either): return %e : $Either } // -- Handle case where all of the payloads become empty. // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @empty_instance(i8 %0) {{.*}} { sil @empty_instance : $@convention(thin) (Either<(), ()>) -> () { // CHECK-NEXT: entry: entry(%e : $Either<(), ()>): retain_value %e : $Either<(), ()> release_value %e : $Either<(), ()> fix_lifetime %e : $Either<(), ()> // CHECK-NEXT: alloca // CHECK-NEXT: trunc i8 {{.*}} to i1 // CHECK-NEXT: llvm.lifetime.start %s = alloc_stack $Either<(), ()> %l = enum $Either<(), ()>, #Either.Left!enumelt, undef : $() // CHECK-NEXT: store i1 false store %l to %s : $*Either<(), ()> %r = enum $Either<(), ()>, #Either.Right!enumelt, undef : $() // CHECK-NEXT: store i1 true store %r to %s : $*Either<(), ()> %a = unchecked_enum_data %l : $Either<(), ()>, #Either.Left!enumelt %b = unchecked_enum_data %r : $Either<(), ()>, #Either.Right!enumelt // CHECK-NEXT: br i1 {{%.*}}, label %[[RIGHT_PRE:[0-9]+]], label %[[LEFT_PRE:[0-9]+]] // CHECK: [[LEFT_PRE]]: // CHECK: br label [[LEFT:%[0-9]+]] // CHECK: [[RIGHT_PRE]]: // CHECK: br label [[RIGHT:%[0-9]+]] switch_enum %e : $Either<(), ()>, case #Either.Left!enumelt: left, case #Either.Right!enumelt: right left(%x : $()): %0 = integer_literal $Builtin.Int8, 0 br next(%0 : $Builtin.Int8) right(%y : $()): %1 = integer_literal $Builtin.Int8, 1 br next(%1 : $Builtin.Int8) // CHECK: phi i8 [ 1, [[RIGHT]] ], [ 0, [[LEFT]] ] next(%z : $Builtin.Int8): dealloc_stack %s : $*Either<(), ()> return undef : $() } // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @empty_instance2(i8 %0) {{.*}} { sil @empty_instance2 : $@convention(thin) (EitherOr<(), ()>) -> () { // CHECK-NEXT: entry: entry(%e : $EitherOr<(), ()>): retain_value %e : $EitherOr<(), ()> release_value %e : $EitherOr<(), ()> fix_lifetime %e : $EitherOr<(), ()> // CHECK-NEXT: alloca // CHECK-NEXT: llvm.lifetime.start %s = alloc_stack $EitherOr<(), ()> // CHECK-NEXT: store i8 0 %l = enum $EitherOr<(), ()>, #EitherOr.Left!enumelt, undef : $() store %l to %s : $*EitherOr<(), ()> // CHECK-NEXT: store i8 1 %r = enum $EitherOr<(), ()>, #EitherOr.Right!enumelt, undef : $() store %r to %s : $*EitherOr<(), ()> // CHECK-NEXT: store i8 2 %m = enum $EitherOr<(), ()>, #EitherOr.Middle!enumelt store %m to %s : $*EitherOr<(), ()> // CHECK-NEXT: store i8 3 %k = enum $EitherOr<(), ()>, #EitherOr.Center!enumelt store %k to %s : $*EitherOr<(), ()> %a = unchecked_enum_data %l : $EitherOr<(), ()>, #EitherOr.Left!enumelt %b = unchecked_enum_data %r : $EitherOr<(), ()>, #EitherOr.Right!enumelt // CHECK-NEXT: switch // CHECK-NEXT: i8 0, label %[[LEFT_PRE:[0-9]+]] // CHECK-NEXT: i8 1, label %[[RIGHT_PRE:[0-9]+]] // CHECK-NEXT: i8 2, label [[MIDDLE:%[0-9]+]] // CHECK-NEXT: i8 3, label [[CENTER:%[0-9]+]] switch_enum %e : $EitherOr<(), ()>, case #EitherOr.Left!enumelt: left, case #EitherOr.Middle!enumelt: middle, case #EitherOr.Center!enumelt: center, case #EitherOr.Right!enumelt: right // CHECK: [[LEFT_PRE]]: // CHECK: br label [[LEFT:%[0-9]+]] // CHECK: [[RIGHT_PRE]]: // CHECK: br label [[RIGHT:%[0-9]+]] left(%x : $()): %0 = integer_literal $Builtin.Int8, 0 br next(%0 : $Builtin.Int8) middle: %1 = integer_literal $Builtin.Int8, 1 br next(%1 : $Builtin.Int8) center: %2 = integer_literal $Builtin.Int8, 2 br next(%2 : $Builtin.Int8) right(%y : $()): %3 = integer_literal $Builtin.Int8, 3 br next(%3 : $Builtin.Int8) next(%z : $Builtin.Int8): // CHECK: phi i8 [ 3, [[RIGHT]] ], [ 2, [[CENTER]] ], [ 1, [[MIDDLE]] ], [ 0, [[LEFT]] ] dealloc_stack %s : $*EitherOr<(), ()> return undef : $() } sil hidden_external @consume_case_test_result : $@convention(thin) (Builtin.Int1, Builtin.Int1, Builtin.Int1, Builtin.Int1) -> () // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @static_multi_payload_case_test // CHECK: call swiftcc void @consume_case_test_result(i1 true, i1 true, i1 true, i1 true) sil @static_multi_payload_case_test : $@convention(thin) () -> () { entry: %0 = integer_literal $Builtin.Int64, 0 %t = integer_literal $Builtin.Int1, 1 %f = integer_literal $Builtin.Int1, 0 %a = enum $EitherOr, #EitherOr.Left!enumelt, %0 : $Builtin.Int64 %x = select_enum %a : $EitherOr, case #EitherOr.Left!enumelt: %t, default %f : $Builtin.Int1 %b = enum $EitherOr, #EitherOr.Right!enumelt, %0 : $Builtin.Int64 %y = select_enum %b : $EitherOr, case #EitherOr.Right!enumelt: %t, default %f : $Builtin.Int1 %c = enum $EitherOr, #EitherOr.Middle!enumelt %z = select_enum %c : $EitherOr, case #EitherOr.Middle!enumelt: %t, default %f : $Builtin.Int1 %d = enum $EitherOr, #EitherOr.Center!enumelt %w = select_enum %d : $EitherOr, case #EitherOr.Center!enumelt: %t, default %f : $Builtin.Int1 %g = function_ref @consume_case_test_result : $@convention(thin) (Builtin.Int1, Builtin.Int1, Builtin.Int1, Builtin.Int1) -> () %r = apply %g(%x, %y, %z, %w) : $@convention(thin) (Builtin.Int1, Builtin.Int1, Builtin.Int1, Builtin.Int1) -> () return %r : $() } // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @static_multi_payload_case_test_empty // CHECK: call swiftcc void @consume_case_test_result(i1 true, i1 true, i1 true, i1 true) sil @static_multi_payload_case_test_empty : $@convention(thin) () -> () { entry: %t = integer_literal $Builtin.Int1, 1 %f = integer_literal $Builtin.Int1, 0 %a = enum $EitherOr<(), ()>, #EitherOr.Left!enumelt, undef : $() %x = select_enum %a : $EitherOr<(), ()>, case #EitherOr.Left!enumelt: %t, default %f : $Builtin.Int1 %b = enum $EitherOr<(), ()>, #EitherOr.Right!enumelt, undef : $() %y = select_enum %b : $EitherOr<(), ()>, case #EitherOr.Right!enumelt: %t, default %f : $Builtin.Int1 %c = enum $EitherOr<(), ()>, #EitherOr.Middle!enumelt %z = select_enum %c : $EitherOr<(), ()>, case #EitherOr.Middle!enumelt: %t, default %f : $Builtin.Int1 %d = enum $EitherOr<(), ()>, #EitherOr.Center!enumelt %w = select_enum %d : $EitherOr<(), ()>, case #EitherOr.Center!enumelt: %t, default %f : $Builtin.Int1 %g = function_ref @consume_case_test_result : $@convention(thin) (Builtin.Int1, Builtin.Int1, Builtin.Int1, Builtin.Int1) -> () %r = apply %g(%x, %y, %z, %w) : $@convention(thin) (Builtin.Int1, Builtin.Int1, Builtin.Int1, Builtin.Int1) -> () return %r : $() } // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dynamic_multi_payload_case_test sil @dynamic_multi_payload_case_test : $@convention(thin) () -> () { entry: %t = integer_literal $Builtin.Int1, 1 %f = integer_literal $Builtin.Int1, 0 %a = alloc_stack $EitherOr // CHECK: [[LEFT:%.*]] = icmp eq i32 {{%.*}}, 0 %x = select_enum_addr %a : $*EitherOr, case #EitherOr.Left!enumelt: %t, default %f : $Builtin.Int1 // CHECK: [[RIGHT:%.*]] = icmp eq i32 {{%.*}}, 1 %y = select_enum_addr %a : $*EitherOr, case #EitherOr.Right!enumelt: %t, default %f : $Builtin.Int1 // CHECK: [[MIDDLE:%.*]] = icmp eq i32 {{%.*}}, 2 %z = select_enum_addr %a : $*EitherOr, case #EitherOr.Middle!enumelt: %t, default %f : $Builtin.Int1 // CHECK: [[CENTER:%.*]] = icmp eq i32 {{%.*}}, 3 %w = select_enum_addr %a : $*EitherOr, case #EitherOr.Center!enumelt: %t, default %f : $Builtin.Int1 // CHECK: call swiftcc void @consume_case_test_result(i1 [[LEFT]], i1 [[RIGHT]], i1 [[MIDDLE]], i1 [[CENTER]]) %g = function_ref @consume_case_test_result : $@convention(thin) (Builtin.Int1, Builtin.Int1, Builtin.Int1, Builtin.Int1) -> () %r = apply %g(%x, %y, %z, %w) : $@convention(thin) (Builtin.Int1, Builtin.Int1, Builtin.Int1, Builtin.Int1) -> () dealloc_stack %a : $*EitherOr return %r : $() } // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dynamic_inject // CHECK: (ptr noalias sret({{.*}}) %0, ptr %T) sil @dynamic_inject : $@convention(thin) () -> @out EitherOr { entry(%e : $*EitherOr): // CHECK: call void @swift_storeEnumTagMultiPayload(ptr {{%.*}}, ptr [[TYPE:%.*]], i32 0) inject_enum_addr %e : $*EitherOr, #EitherOr.Left!enumelt // CHECK: call void @swift_storeEnumTagMultiPayload(ptr {{%.*}}, ptr [[TYPE]], i32 2) inject_enum_addr %e : $*EitherOr, #EitherOr.Middle!enumelt // CHECK: call void @swift_storeEnumTagMultiPayload(ptr {{%.*}}, ptr [[TYPE]], i32 3) inject_enum_addr %e : $*EitherOr, #EitherOr.Center!enumelt // CHECK: call void @swift_storeEnumTagMultiPayload(ptr {{%.*}}, ptr [[TYPE]], i32 1) inject_enum_addr %e : $*EitherOr, #EitherOr.Right!enumelt return undef : $() } // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dynamic_project // CHECK: (ptr noalias sret({{.*}}) %0, ptr %T) sil @dynamic_project : $@convention(thin) () -> @out EitherOr { entry(%e : $*EitherOr): %l = unchecked_take_enum_data_addr %e : $*EitherOr, #EitherOr.Left!enumelt %r = unchecked_take_enum_data_addr %e : $*EitherOr, #EitherOr.Right!enumelt return undef : $() } // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dynamic_switch // CHECK: (ptr noalias sret({{.*}}) %0, ptr %T) sil @dynamic_switch : $@convention(thin) () -> @out EitherOr { entry(%e : $*EitherOr): // CHECK: [[TAG:%.*]] = call i32 @swift_getEnumCaseMultiPayload // CHECK: switch i32 [[TAG]] // CHECK-NEXT: i32 0, label %[[LEFT:[0-9]+]] // CHECK-NEXT: i32 1, label %[[RIGHT:[0-9]+]] // CHECK-NEXT: i32 2, label %[[MIDDLE:[0-9]+]] // CHECK-NEXT: i32 3, label %[[CENTER:[0-9]+]] switch_enum_addr %e : $*EitherOr, case #EitherOr.Left!enumelt: left, case #EitherOr.Middle!enumelt: middle, case #EitherOr.Center!enumelt: center, case #EitherOr.Right!enumelt: right // CHECK: [[LEFT]]: left: %0 = integer_literal $Builtin.Int8, 0 br next(%0 : $Builtin.Int8) // CHECK: [[MIDDLE]]: middle: %1 = integer_literal $Builtin.Int8, 1 br next(%1 : $Builtin.Int8) // CHECK: [[CENTER]]: center: %2 = integer_literal $Builtin.Int8, 2 br next(%2 : $Builtin.Int8) // CHECK: [[RIGHT]]: right: %3 = integer_literal $Builtin.Int8, 3 br next(%3 : $Builtin.Int8) // CHECK: phi i8 [ 3, %[[RIGHT]] ], [ 2, %[[CENTER]] ], [ 1, %[[MIDDLE]] ], [ 0, %[[LEFT]] ] next(%x : $Builtin.Int8): return undef : $() } // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dynamic_value_semantics // CHECK: (ptr noalias sret({{.*}}) %0, ptr noalias %1, ptr %T) sil @dynamic_value_semantics : $@convention(thin) (@in EitherOr) -> @out EitherOr { entry(%a : $*EitherOr, %b : $*EitherOr): // CHECK: [[TAG:%.*]] = call i32 @swift_getEnumCaseMultiPayload // -- only the Left branch of this instance needs cleanup // CHECK: [[COND:%.*]] = icmp ne i32 [[TAG]], 0 // CHECK-NEXT: br i1 [[COND]], label %[[NOOP:[0-9]+]], label %[[LEFT:[0-9]+]] // CHECK: [[LEFT]]: // CHECK: [[DESTROYADDR:%[0-9]+]] = getelementptr inbounds ptr, ptr {{.*}}, i32 1 // CHECK: [[DESTROY:%[^,]+]] = load ptr, ptr [[DESTROYADDR]] // CHECK: call void [[DESTROY]](ptr noalias {{%[0-9]+}}, ptr %T) // CHECK: br label %[[NOOP]] // CHECK: [[NOOP]]: destroy_addr %a : $*EitherOr // CHECK: [[TAG:%.*]] = call i32 @swift_getEnumCaseMultiPayload // -- only the Left branch of this instance needs nontrivial take // CHECK: [[COND:%.*]] = icmp ne i32 [[TAG]], 0 // CHECK-NEXT: br i1 [[COND]], label %[[TRIVIAL:[0-9]+]], label %[[LEFT:[0-9]+]] // CHECK: [[LEFT]]: // CHECK: [[INITWITHTAKEADDR:%[0-9]+]] = getelementptr inbounds ptr, ptr {{.*}}, i32 4 // CHECK: [[INITWITHTAKE:%[^,]+]] = load ptr, ptr [[INITWITHTAKEADDR]] // CHECK: {{%[0-9]+}} = call ptr [[INITWITHTAKE]](ptr noalias {{%[0-9]+}}, ptr noalias {{%[0-9]+}}, ptr %T) // CHECK: br label %[[DONE:[0-9]+]] // CHECK: [[TRIVIAL]]: // CHECK: call void @llvm.memcpy // CHECK: br label %[[DONE]] // CHECK: [[DONE]]: copy_addr [take] %a to [init] %b : $*EitherOr copy_addr [take] %a to %b : $*EitherOr copy_addr %a to [init] %b : $*EitherOr copy_addr %a to %b : $*EitherOr return undef : $() } // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dynamic_value_semantics2 // CHECK: (ptr noalias sret({{.*}}) %0, ptr noalias %1, ptr %T) sil @dynamic_value_semantics2 : $@convention(thin) (@in EitherOr) -> @out EitherOr { entry(%a : $*EitherOr, %b : $*EitherOr): // CHECK: [[TAG:%.*]] = call i32 @swift_getEnumCaseMultiPayload // CHECK: switch i32 [[TAG]], label %[[NOOP:[0-9]+]] [ // CHECK-NEXT: i32 0, label %[[LEFT:[0-9]+]] // CHECK-NEXT: i32 1, label %[[RIGHT:[0-9]+]] // CHECK-NEXT: ] // CHECK: [[LEFT]]: // CHECK: [[DESTROYADDR:%[0-9]+]] = getelementptr inbounds ptr, ptr {{.*}}, i32 1 // CHECK: [[DESTROY:%[^,]+]] = load ptr, ptr [[DESTROYADDR]] // CHECK: call void [[DESTROY]](ptr noalias {{%[0-9]+}}, ptr %T) // CHECK: br label %[[NOOP]] // CHECK: [[RIGHT]]: // CHECK: call void @swift_release // CHECK: [[NOOP]]: destroy_addr %a : $*EitherOr // CHECK: [[TAG:%.*]] = call i32 @swift_getEnumCaseMultiPayload // -- only the Left branch of this instance needs cleanup // CHECK: [[COND:%.*]] = icmp ne i32 [[TAG]], 0 // CHECK-NEXT: br i1 [[COND]], label %[[TRIVIAL:[0-9]+]], label %[[LEFT:[0-9]+]] // CHECK: [[LEFT]]: // CHECK: [[INITWITHTAKEADDR:%[0-9]+]] = getelementptr inbounds ptr, ptr {{.*}}, i32 4 // CHECK: [[INITWITHTAKE:%[^,]+]] = load ptr, ptr [[INITWITHTAKEADDR]] // CHECK: {{%[0-9]+}} = call ptr [[INITWITHTAKE]](ptr noalias {{%[0-9]+}}, ptr noalias {{%[0-9]+}}, ptr %T) // CHECK: br label %[[DONE:[0-9]+]] // CHECK: [[TRIVIAL]]: // CHECK: call void @llvm.memcpy // CHECK: br label %[[DONE]] // CHECK: [[DONE]]: copy_addr [take] %a to [init] %b : $*EitherOr // CHECK: [[TAG:%.*]] = call i32 @swift_getEnumCaseMultiPayload // CHECK: switch i32 [[TAG]], label %[[TRIVIAL:[0-9]+]] [ // -- both branches have nontrivial copy // CHECK-NEXT: i32 0, label %[[LEFT:[0-9]+]] // CHECK-NEXT: i32 1, label %[[RIGHT:[0-9]+]] // CHECK-NEXT: ] // CHECK: [[LEFT]]: // CHECK: [[INITWITHCOPYADDR:%[0-9]+]] = getelementptr inbounds ptr, ptr {{.*}}, i32 2 // CHECK: [[INITWITHCOPY:%[^,]+]] = load ptr, ptr [[INITWITHCOPYADDR]] // CHECK: {{%[0-9]+}} = call ptr [[INITWITHCOPY]](ptr noalias {{%[0-9]+}}, ptr noalias {{%[0-9]+}}, ptr %T) // CHECK: br label %[[DONE:[0-9]+]] // CHECK: [[RIGHT]]: // CHECK: call ptr @swift_retain // CHECK: br label %[[DONE:[0-9]+]] // CHECK: [[TRIVIAL]]: // CHECK: call void @llvm.memcpy // CHECK: br label %[[DONE]] // CHECK: [[DONE]]: copy_addr %a to [init] %b : $*EitherOr return undef : $() } // CHECK: define{{( dllexport)?}}{{( protected)?}} internal ptr @"$s26enum_dynamic_multi_payload8EitherOrOMi"(ptr %0, ptr %1, ptr %2) {{.*}} { // CHECK: [[METADATA:%.*]] = call ptr @swift_allocateGenericValueMetadata // CHECK: define{{( protected)?}} internal swiftcc %swift.metadata_response @"$s26enum_dynamic_multi_payload8EitherOrOMr" // CHECK-SAME: (ptr [[METADATA:%.*]], ptr %0, ptr %1) {{.*}} { // CHECK: [[BUF:%.*]] = alloca [2 x ptr] // CHECK: [[BUF0:%.*]] = getelementptr {{.*}} [[BUF]], i32 0, i32 0 // CHECK: [[T0:%.*]] = call{{( tail)?}} swiftcc %swift.metadata_response @swift_checkMetadataState([[INT]] 319, ptr %T) // CHECK-NEXT: [[T_CHECKED:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 // CHECK-NEXT: [[T_STATUS:%.*]] = extractvalue %swift.metadata_response [[T0]], 1 // CHECK-NEXT: [[T_OK:%.*]] = icmp ule [[INT]] [[T_STATUS]], 63 // CHECK-NEXT: br i1 [[T_OK]], // CHECK: [[T1:%.*]] = getelementptr inbounds ptr, ptr [[T_CHECKED]] // CHECK-NEXT: [[VALUE_WITNESSES:%.*]] = load ptr, ptr [[T1]] // CHECK-arm64e-NEXT: ptrtoint ptr [[T1]] to i64 // CHECK-arm64e-NEXT: call i64 @llvm.ptrauth.blend // CHECK-arm64e: [[VALUE_WITNESSES:%.*]] = inttoptr i64 {{%.*}} to ptr // CHECK-NEXT: [[LAYOUT:%.*]] = getelementptr inbounds ptr, ptr [[VALUE_WITNESSES]], i32 8 // CHECK-NEXT: store ptr [[LAYOUT]], {{.*}} [[BUF0]] // CHECK: [[BUF1:%.*]] = getelementptr {{.*}} [[BUF]], i32 0, i32 1 // CHECK: [[T0:%.*]] = call{{( tail)?}} swiftcc %swift.metadata_response @swift_checkMetadataState([[INT]] 319, ptr %U) // CHECK-NEXT: [[U_CHECKED:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 // CHECK-NEXT: [[U_STATUS:%.*]] = extractvalue %swift.metadata_response [[T0]], 1 // CHECK-NEXT: [[U_OK:%.*]] = icmp ule [[INT]] [[U_STATUS]], 63 // CHECK-NEXT: br i1 [[U_OK]], // CHECK: [[T1:%.*]] = getelementptr inbounds ptr, ptr [[U_CHECKED]] // CHECK-NEXT: [[VALUE_WITNESSES:%.*]] = load ptr, ptr [[T1]] // CHECK-arm64e-NEXT: ptrtoint ptr [[T1]] to i64 // CHECK-arm64e-NEXT: call i64 @llvm.ptrauth.blend // CHECK-arm64e: [[VALUE_WITNESSES:%.*]] = inttoptr i64 {{%.*}} to ptr // CHECK-NEXT: [[LAYOUT:%.*]] = getelementptr inbounds ptr, ptr [[VALUE_WITNESSES]], i32 8 // CHECK-NEXT: store ptr [[LAYOUT]], {{.*}} [[BUF1]] // CHECK: call void @swift_initEnumMetadataMultiPayload(ptr [[METADATA]], {{i(32|64)}} 0, {{i(32|64)}} 2, ptr [[BUF0]])