Files
swift-mirror/test/SILOptimizer/address_lowering_polymorphic_builtins.sil
Jakub Florek 5af4cdd9fe Opaque Values: Pass builtin arguments at their parameters' declared ownership
Under `-enable-sil-opaque-values`, SILGen copied every builtin argument to +1 and forwarded it into the builtin. Polymorphic builtins such as `Builtin.generic_add` only borrow their operands. Nothing consumed the copy, so the OSSA verifier reported a leaked owned value. SILGen now passes each argument at the ownership its parameter declares in `Builtins.cpp`, instead of special-casing polymorphic builtins. An assertion checks that each declared ownership agrees with the builtin's OperandOwnership classification. AddressLowering now rewrites the value form of a polymorphic builtin into the address form with an indirect result.

Adds opaque-values RUN lines to the polymorphic builtin tests in SILGen, IRGen and SILOptimizer, and a SIL test for the AddressLowering rewrite. Removes the opaque-values XFAIL from `test/Interpreter/polymorphic_builtins.swift`.

Resolves rdar://180980544
2026-09-25 12:51:59 +01:00

340 lines
18 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -address-lowering -enable-sil-opaque-values -module-name Swift -sil-verify-all %s | %FileCheck %s
// Polymorphic builtins (e.g. "generic_add") only borrow their operands (they
// never appear in AddressLowering as a "consuming" def/use), so AddressLowering
// must materialize an address for each operand and for the result without
// otherwise touching their ownership. This directly exercises AddressLowering's
// DefRewriter/UseRewriter handling for every Generic* polymorphic builtin,
// independently of SILGen and of the unrelated "unspecialized polymorphic
// builtin" diagnostic that a full -emit-sil run would hit for generic, non-
// transparent uses of these builtins (see test/SILGen/polymorphic_builtins.swift).
//
// The genericXTest functions below cover the builtins one by one in the
// simplest shape: the result is the caller's @out argument. The shapeX
// functions at the end instead vary where the result's storage comes from,
// which is what exercises AddressMaterialization.
sil_stage raw
import Builtin
enum Enum<T> {
case some(T)
case none
}
sil @makeT : $@convention(thin) <T> () -> @out T
// CHECK-LABEL: sil [ossa] @genericAddTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_add"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericAddTest'
sil [ossa] @genericAddTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_add"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericAndTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_and"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericAndTest'
sil [ossa] @genericAndTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_and"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericAshrTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_ashr"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericAshrTest'
sil [ossa] @genericAshrTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_ashr"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericFaddTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_fadd"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericFaddTest'
sil [ossa] @genericFaddTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_fadd"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericFdivTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_fdiv"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericFdivTest'
sil [ossa] @genericFdivTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_fdiv"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericFmulTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_fmul"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericFmulTest'
sil [ossa] @genericFmulTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_fmul"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericFremTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_frem"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericFremTest'
sil [ossa] @genericFremTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_frem"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericFsubTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_fsub"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericFsubTest'
sil [ossa] @genericFsubTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_fsub"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericLshrTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_lshr"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericLshrTest'
sil [ossa] @genericLshrTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_lshr"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericMulTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_mul"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericMulTest'
sil [ossa] @genericMulTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_mul"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericOrTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_or"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericOrTest'
sil [ossa] @genericOrTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_or"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericSdivExactTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_sdiv_exact"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericSdivExactTest'
sil [ossa] @genericSdivExactTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_sdiv_exact"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericSdivTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_sdiv"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericSdivTest'
sil [ossa] @genericSdivTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_sdiv"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericShlTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_shl"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericShlTest'
sil [ossa] @genericShlTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_shl"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericSremTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_srem"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericSremTest'
sil [ossa] @genericSremTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_srem"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericSubTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_sub"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericSubTest'
sil [ossa] @genericSubTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_sub"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericUdivExactTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_udiv_exact"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericUdivExactTest'
sil [ossa] @genericUdivExactTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_udiv_exact"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericUdivTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_udiv"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericUdivTest'
sil [ossa] @genericUdivTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_udiv"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericUremTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_urem"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericUremTest'
sil [ossa] @genericUremTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_urem"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @genericXorTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: builtin "generic_xor"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'genericXorTest'
sil [ossa] @genericXorTest : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_xor"<T>(%0 : $T, %1 : $T) : $T
return %2 : $T
}
// The result of the inner builtin is not the caller's @out argument, so it
// needs storage of its own.
// CHECK-LABEL: sil [ossa] @shapeChained : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: [[TMP:%.*]] = alloc_stack $T
// CHECK: builtin "generic_mul"<T>([[TMP]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK: builtin "generic_add"<T>([[RESULT]] : $*T, [[TMP]] : $*T, [[ARG1]] : $*T) : $()
// CHECK: destroy_addr [[TMP]] : $*T
// CHECK: dealloc_stack [[TMP]] : $*T
// CHECK-LABEL: } // end sil function 'shapeChained'
sil [ossa] @shapeChained : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_mul"<T>(%0 : $T, %1 : $T) : $T
%3 = builtin "generic_add"<T>(%2 : $T, %1 : $T) : $T
destroy_value %2 : $T
return %3 : $T
}
// An unused result still needs storage; nothing consumes it, so the storage is
// destroyed in place.
// CHECK-LABEL: sil [ossa] @shapeDeadResult : $@convention(thin) <T> (@in_guaranteed T) -> () {
// CHECK: bb0([[ARG:%.*]] : $*T):
// CHECK: [[TMP:%.*]] = alloc_stack $T
// CHECK: builtin "generic_xor"<T>([[TMP]] : $*T, [[ARG]] : $*T, [[ARG]] : $*T) : $()
// CHECK: destroy_addr [[TMP]] : $*T
// CHECK: dealloc_stack [[TMP]] : $*T
// CHECK-LABEL: } // end sil function 'shapeDeadResult'
sil [ossa] @shapeDeadResult : $@convention(thin) <T> (@in_guaranteed T) -> () {
bb0(%0 : @guaranteed $T):
%1 = builtin "generic_xor"<T>(%0 : $T, %0 : $T) : $T
destroy_value %1 : $T
%2 = tuple ()
return %2 : $()
}
// The result's storage is a projection out of an enum payload.
// CHECK-LABEL: sil [ossa] @shapeEnumPayload : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out Enum<T> {
// CHECK: bb0([[RESULT:%.*]] : $*Enum<T>, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: [[PAYLOAD:%.*]] = init_enum_data_addr [[RESULT]] : $*Enum<T>, #Enum.some!enumelt
// CHECK: builtin "generic_add"<T>([[PAYLOAD]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK: inject_enum_addr [[RESULT]] : $*Enum<T>, #Enum.some!enumelt
// CHECK-LABEL: } // end sil function 'shapeEnumPayload'
sil [ossa] @shapeEnumPayload : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out Enum<T> {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_add"<T>(%0 : $T, %1 : $T) : $T
%3 = enum $Enum<T>, #Enum.some!enumelt, %2 : $T
return %3 : $Enum<T>
}
// An owned operand is only borrowed by the builtin: its storage is read in
// place and destroyed afterwards, not consumed by the builtin.
// CHECK-LABEL: sil [ossa] @shapeOwnedOperand : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG:%.*]] : $*T):
// CHECK: [[TMP:%.*]] = alloc_stack $T
// CHECK: apply {{%.*}}<T>([[TMP]])
// CHECK: builtin "generic_add"<T>([[RESULT]] : $*T, [[TMP]] : $*T, [[ARG]] : $*T) : $()
// CHECK: destroy_addr [[TMP]] : $*T
// CHECK: dealloc_stack [[TMP]] : $*T
// CHECK-LABEL: } // end sil function 'shapeOwnedOperand'
sil [ossa] @shapeOwnedOperand : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T):
%1 = function_ref @makeT : $@convention(thin) <τ_0_0> () -> @out τ_0_0
%2 = apply %1<T>() : $@convention(thin) <τ_0_0> () -> @out τ_0_0
%3 = builtin "generic_add"<T>(%2 : $T, %0 : $T) : $T
destroy_value %2 : $T
return %3 : $T
}
// Results flowing into a phi share the phi's storage, so each predecessor
// writes the caller's @out argument directly and the phi argument goes away.
// CHECK-LABEL: sil [ossa] @shapePhi : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T, Builtin.Int1) -> @out T {
// CHECK: bb0([[RESULT:%.*]] : $*T, [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T, {{%.*}} : $Builtin.Int1):
// CHECK: bb1:
// CHECK: builtin "generic_add"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK: br bb3
// CHECK: bb2:
// CHECK: builtin "generic_sub"<T>([[RESULT]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK: br bb3
// The merge block prints without an argument list, i.e. the phi is gone.
// CHECK: bb3:
// CHECK-LABEL: } // end sil function 'shapePhi'
sil [ossa] @shapePhi : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T, Builtin.Int1) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T, %2 : $Builtin.Int1):
cond_br %2, bb1, bb2
bb1:
%3 = builtin "generic_add"<T>(%0 : $T, %1 : $T) : $T
br bb3(%3 : $T)
bb2:
%4 = builtin "generic_sub"<T>(%0 : $T, %1 : $T) : $T
br bb3(%4 : $T)
bb3(%5 : @owned $T):
return %5 : $T
}
// Each result's storage is a projection into the caller's @out tuple.
// CHECK-LABEL: sil [ossa] @shapeTuple : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out (T, T) {
// CHECK: bb0([[RESULT:%.*]] : $*(T, T), [[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
// CHECK: [[ELT0:%.*]] = tuple_element_addr [[RESULT]] : $*(T, T), 0
// CHECK: builtin "generic_add"<T>([[ELT0]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK: [[ELT1:%.*]] = tuple_element_addr [[RESULT]] : $*(T, T), 1
// CHECK: builtin "generic_sub"<T>([[ELT1]] : $*T, [[ARG0]] : $*T, [[ARG1]] : $*T) : $()
// CHECK-LABEL: } // end sil function 'shapeTuple'
sil [ossa] @shapeTuple : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> @out (T, T) {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T):
%2 = builtin "generic_add"<T>(%0 : $T, %1 : $T) : $T
%3 = builtin "generic_sub"<T>(%0 : $T, %1 : $T) : $T
%4 = tuple (%2 : $T, %3 : $T)
return %4 : $(T, T)
}