Skip to content

Commit c2dad28

Browse files
authored
Merge pull request swiftlang#20043 from eeckstein/utf8-string-literals
support static large string literal generation for the new UTF8 String implementation
2 parents e7c2bf0 + ac2b572 commit c2dad28

File tree

8 files changed

+95
-13
lines changed

8 files changed

+95
-13
lines changed

include/swift/SIL/SILGlobalVariable.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ class SILGlobalVariable
158158
static bool isValidStaticInitializerInst(const SILInstruction *I,
159159
SILModule &M);
160160

161+
/// Returns the usub_with_overflow builtin if \p TE extracts the result of
162+
/// such a subtraction, which is required to have an integer_literal as right
163+
/// operand.
164+
static BuiltinInst *getOffsetSubtract(const TupleExtractInst *TE, SILModule &M);
165+
161166
void dropAllReferences() {
162167
StaticInitializerBlock.dropAllReferences();
163168
}

include/swift/SIL/SILValue.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
280280
inline Operand *getSingleUse() const;
281281

282282
template <class T>
283-
inline T *getSingleUserOfType();
283+
inline T *getSingleUserOfType() const;
284284

285285
/// Return the instruction that defines this value, or null if it is
286286
/// not defined by an instruction.
@@ -706,7 +706,7 @@ inline Operand *ValueBase::getSingleUse() const {
706706
}
707707

708708
template <class T>
709-
inline T *ValueBase::getSingleUserOfType() {
709+
inline T *ValueBase::getSingleUserOfType() const {
710710
T *Result = nullptr;
711711
for (auto *Op : getUses()) {
712712
if (auto *Tmp = dyn_cast<T>(Op->getUser())) {

lib/IRGen/GenConstant.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,20 +94,30 @@ static llvm::Constant *emitConstantValue(IRGenModule &IGM, SILValue operand) {
9494
return llvm::ConstantExpr::getZExtOrBitCast(value, IGM.getStorageType(BI->getType()));
9595
}
9696
case BuiltinValueKind::StringObjectOr: {
97-
llvm::Constant *lhs = emitConstantValue(IGM, BI->getArguments()[0]);
98-
llvm::Constant *rhs = emitConstantValue(IGM, BI->getArguments()[1]);
9997
// It is a requirement that the or'd bits in the left argument are
10098
// initialized with 0. Therefore the or-operation is equivalent to an
10199
// addition. We need an addition to generate a valid relocation.
100+
llvm::Constant *rhs = emitConstantValue(IGM, BI->getArguments()[1]);
101+
if (auto *TE = dyn_cast<TupleExtractInst>(BI->getArguments()[0])) {
102+
// Handle StringObjectOr(tuple_extract(usub_with_overflow(x, offset)), bits)
103+
// This pattern appears in UTF8 String literal construction.
104+
// Generate the equivalent: add(x, sub(bits - offset)
105+
BuiltinInst *SubtrBI =
106+
SILGlobalVariable::getOffsetSubtract(TE, IGM.getSILModule());
107+
assert(SubtrBI && "unsupported argument of StringObjectOr");
108+
auto *ptr = emitConstantValue(IGM, SubtrBI->getArguments()[0]);
109+
auto *offset = emitConstantValue(IGM, SubtrBI->getArguments()[1]);
110+
auto *totalOffset = llvm::ConstantExpr::getSub(rhs, offset);
111+
return llvm::ConstantExpr::getAdd(ptr, totalOffset);
112+
}
113+
llvm::Constant *lhs = emitConstantValue(IGM, BI->getArguments()[0]);
102114
return llvm::ConstantExpr::getAdd(lhs, rhs);
103115
}
104116
default:
105117
llvm_unreachable("unsupported builtin for constant expression");
106118
}
107119
} else if (auto *VTBI = dyn_cast<ValueToBridgeObjectInst>(operand)) {
108-
auto *SI = cast<StructInst>(VTBI->getOperand());
109-
assert(SI->getElements().size() == 1);
110-
auto *val = emitConstantValue(IGM, SI->getElements()[0]);
120+
auto *val = emitConstantValue(IGM, VTBI->getOperand());
111121
auto *sTy = IGM.getTypeInfo(VTBI->getType()).getStorageType();
112122
return llvm::ConstantExpr::getIntToPtr(val, sTy);
113123
} else {

lib/SIL/SILGlobalVariable.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,31 @@ SILInstruction *SILGlobalVariable::getStaticInitializerValue() {
7878
return &StaticInitializerBlock.back();
7979
}
8080

81+
BuiltinInst *SILGlobalVariable::getOffsetSubtract(const TupleExtractInst *TE,
82+
SILModule &M) {
83+
84+
// Match the pattern:
85+
// tuple_extract(usub_with_overflow(x, integer_literal, integer_literal 0), 0)
86+
87+
if (TE->getFieldNo() != 0)
88+
return nullptr;
89+
90+
auto *BI = dyn_cast<BuiltinInst>(TE->getOperand());
91+
if (!BI)
92+
return nullptr;
93+
if (M.getBuiltinInfo(BI->getName()).ID != BuiltinValueKind::USubOver)
94+
return nullptr;
95+
96+
if (!isa<IntegerLiteralInst>(BI->getArguments()[1]))
97+
return nullptr;
98+
99+
auto *overflowFlag = dyn_cast<IntegerLiteralInst>(BI->getArguments()[2]);
100+
if (!overflowFlag || !overflowFlag->getValue().isNullValue())
101+
return nullptr;
102+
103+
return BI;
104+
}
105+
81106
bool SILGlobalVariable::isValidStaticInitializerInst(const SILInstruction *I,
82107
SILModule &M) {
83108
switch (I->getKind()) {
@@ -99,11 +124,27 @@ bool SILGlobalVariable::isValidStaticInitializerInst(const SILInstruction *I,
99124
break;
100125
case BuiltinValueKind::ZExtOrBitCast:
101126
return true;
127+
case BuiltinValueKind::USubOver: {
128+
// Handle StringObjectOr(tuple_extract(usub_with_overflow(x, offset)), bits)
129+
// This pattern appears in UTF8 String literal construction.
130+
auto *TE = bi->getSingleUserOfType<TupleExtractInst>();
131+
return TE && getOffsetSubtract(TE, M);
132+
}
102133
default:
103134
break;
104135
}
105136
return false;
106137
}
138+
case SILInstructionKind::TupleExtractInst: {
139+
// Handle StringObjectOr(tuple_extract(usub_with_overflow(x, offset)), bits)
140+
// This pattern appears in UTF8 String literal construction.
141+
auto *TE = cast<TupleExtractInst>(I);
142+
if (!getOffsetSubtract(TE, M))
143+
return false;
144+
auto *BI = TE->getSingleUserOfType<BuiltinInst>();
145+
return BI &&
146+
M.getBuiltinInfo(BI->getName()).ID == BuiltinValueKind::StringObjectOr;
147+
}
107148
case SILInstructionKind::StringLiteralInst:
108149
switch (cast<StringLiteralInst>(I)->getEncoding()) {
109150
case StringLiteralInst::Encoding::Bytes:

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -867,9 +867,15 @@ static ManagedValue emitBuiltinValueToBridgeObject(SILGenFunction &SGF,
867867
assert(args.size() == 1 && "ValueToBridgeObject should have one argument");
868868
assert(subs.getReplacementTypes().size() == 1 &&
869869
"ValueToBridgeObject should have one sub");
870-
auto &fromTL = SGF.getTypeLowering(subs.getReplacementTypes()[0]);
871-
assert(fromTL.isTrivial() && "Expected a trivial type");
872-
(void)fromTL;
870+
871+
Type argTy = subs.getReplacementTypes()[0];
872+
if (!argTy->is<BuiltinIntegerType>()) {
873+
SGF.SGM.diagnose(loc, diag::invalid_sil_builtin,
874+
"argument to builtin should be a builtin integer");
875+
SILType objPointerType = SILType::getBridgeObjectType(SGF.F.getASTContext());
876+
SILValue undef = SILUndef::get(objPointerType, SGF.SGM.M);
877+
return ManagedValue::forUnmanaged(undef);
878+
}
873879

874880
SILValue result = SGF.B.createValueToBridgeObject(loc, args[0].getValue());
875881
return SGF.emitManagedRetain(loc, result);

stdlib/public/core/Builtin.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ public func _bridgeObject(
458458
@inlinable
459459
public func _bridgeObject(fromTagged x: UInt) -> Builtin.BridgeObject {
460460
_sanityCheck(x & _bridgeObjectTaggedPointerBits != 0)
461-
let object: Builtin.BridgeObject = Builtin.valueToBridgeObject(x)
461+
let object: Builtin.BridgeObject = Builtin.valueToBridgeObject(x._value)
462462
_sanityCheck(_isTaggedObject(object))
463463
return object
464464
}

test/IRGen/static_initializer.sil

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,25 @@ sil_global @static_array_with_empty_element : $TestArrayStorage = {
103103
}
104104
// CHECK: @static_array_with_empty_element = {{( dllexport)?}}{{(protected )?}}global %T18static_initializer16TestArrayStorageC_tailelems3c { [1 x i64] zeroinitializer, %T18static_initializer16TestArrayStorageC_tailelems3 <{ %swift.refcounted zeroinitializer, %Ts5Int32V <{ i32 2 }>, [1 x i8] undef, [1 x i8] undef }> }, align 8
105105

106+
struct MyString {
107+
var buffer: Builtin.BridgeObject
108+
}
109+
110+
sil_global [let] @string_with_offset : $MyString = {
111+
%0 = integer_literal $Builtin.Int64, -9223372036854775808
112+
%1 = integer_literal $Builtin.Int1, 0
113+
%2 = integer_literal $Builtin.Int64, 32
114+
%3 = string_literal utf8 "abc123asd3sdj3basfasdf"
115+
%4 = builtin "ptrtoint_Word"(%3 : $Builtin.RawPointer) : $Builtin.Word
116+
%5 = builtin "zextOrBitCast_Word_Int64"(%4 : $Builtin.Word) : $Builtin.Int64
117+
%6 = builtin "usub_with_overflow_Int64"(%5 : $Builtin.Int64, %2 : $Builtin.Int64, %1 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
118+
%7 = tuple_extract %6 : $(Builtin.Int64, Builtin.Int1), 0
119+
%8 = builtin "stringObjectOr_Int64"(%7 : $Builtin.Int64, %0 : $Builtin.Int64) : $Builtin.Int64
120+
%9 = value_to_bridge_object %8 : $Builtin.Int64
121+
%initval = struct $MyString (%9 : $Builtin.BridgeObject)
122+
}
123+
// CHECK: @string_with_offset = {{.*global .*}} <{ %swift.bridge* inttoptr (i64 add (i64 ptrtoint ([23 x i8]* @0 to i64), i64 9223372036854775776) to %swift.bridge*) }>, align 8
124+
106125
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8* @_TF2cha1xSi() {{.*}} {
107126
// CHECK-NEXT: entry:
108127
// CHECK-NEXT: ret i8* bitcast (%Ts5Int32V* @"$s2ch1xSiv" to i8*)

test/SILGen/builtins.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -809,12 +809,13 @@ func once(control: Builtin.RawPointer) {
809809

810810
// CHECK-LABEL: sil hidden @$s8builtins19valueToBridgeObjectyBbSuF : $@convention(thin) (UInt) -> @owned Builtin.BridgeObject {
811811
// CHECK: bb0([[UINT:%.*]] : @trivial $UInt):
812-
// CHECK: [[CAST:%.*]] = value_to_bridge_object [[UINT]] : $UInt
812+
// CHECK: [[BI:%.*]] = struct_extract [[UINT]] : $UInt, #UInt._value
813+
// CHECK: [[CAST:%.*]] = value_to_bridge_object [[BI]]
813814
// CHECK: [[RET:%.*]] = copy_value [[CAST]] : $Builtin.BridgeObject
814815
// CHECK: return [[RET]] : $Builtin.BridgeObject
815816
// CHECK: } // end sil function '$s8builtins19valueToBridgeObjectyBbSuF'
816817
func valueToBridgeObject(_ x: UInt) -> Builtin.BridgeObject {
817-
return Builtin.valueToBridgeObject(x)
818+
return Builtin.valueToBridgeObject(x._value)
818819
}
819820

820821
// CHECK-LABEL: sil hidden @$s8builtins10assumeTrueyyBi1_F

0 commit comments

Comments
 (0)