Skip to content

Commit e8f248a

Browse files
committed
SILCombine: propagate the extra-bits of StringObjects if they are read right after creation.
For example: %0 = string_literal "abc" %1 = integer_literal 0x8000000000000000 %2 = builtin "stringObjectOr_Int64" (%0, %1) %3 = integer_literal 0x4000000000000000 %4 = builtin "and_Int64" (%2, %3) In this case we know that %4 is 0.
1 parent 1c8f107 commit e8f248a

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ class SILCombiner :
198198
SILInstruction *visitPartialApplyInst(PartialApplyInst *AI);
199199
SILInstruction *visitApplyInst(ApplyInst *AI);
200200
SILInstruction *visitTryApplyInst(TryApplyInst *AI);
201+
SILInstruction *optimizeStringObject(BuiltinInst *BI);
201202
SILInstruction *visitBuiltinInst(BuiltinInst *BI);
202203
SILInstruction *visitCondFailInst(CondFailInst *CFI);
203204
SILInstruction *visitStrongRetainInst(StrongRetainInst *SRI);

lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,87 @@ SILInstruction *optimizeBitOp(BuiltinInst *BI,
430430
return nullptr;
431431
}
432432

433+
/// Returns a 64-bit integer constant if \p op is an integer_literal instruction
434+
/// with a value which fits into 64 bits.
435+
static Optional<uint64_t> getIntConst(SILValue op) {
436+
if (auto *ILI = dyn_cast<IntegerLiteralInst>(op)) {
437+
if (ILI->getValue().getActiveBits() <= 64)
438+
return ILI->getValue().getZExtValue();
439+
}
440+
return None;
441+
}
442+
443+
/// Optimize the bit extract of a string object. Example in SIL pseudo-code,
444+
/// omitting the type-conversion instructions:
445+
///
446+
/// %0 = string_literal
447+
/// %1 = integer_literal 0x8000000000000000
448+
/// %2 = builtin "stringObjectOr_Int64" (%0, %1)
449+
/// %3 = integer_literal 0x4000000000000000
450+
/// %4 = builtin "and_Int64" (%2, %3)
451+
///
452+
/// optimizes to an integer_literal of 0.
453+
SILInstruction *SILCombiner::optimizeStringObject(BuiltinInst *BI) {
454+
assert(BI->getBuiltinInfo().ID == BuiltinValueKind::And);
455+
auto AndOp = getIntConst(BI->getArguments()[1]);
456+
if (!AndOp)
457+
return nullptr;
458+
459+
uint64_t andBits = AndOp.getValue();
460+
461+
// TODO: It's bad that we have to hardcode the payload bit mask here.
462+
// Instead we should introduce builtins (or instructions) to extract the
463+
// payload and extra bits, respectively.
464+
const uint64_t payloadBits = 0x00ffffffffffffffll;
465+
if ((andBits & payloadBits) != 0)
466+
return nullptr;
467+
468+
uint64_t setBits = 0;
469+
SILValue val = BI->getArguments()[0];
470+
while (val->getKind() != ValueKind::StringLiteralInst) {
471+
switch (val->getKind()) {
472+
// Look through all the type conversion and projection instructions.
473+
case ValueKind::StructExtractInst:
474+
case ValueKind::UncheckedTrivialBitCastInst:
475+
case ValueKind::ValueToBridgeObjectInst:
476+
val = cast<SingleValueInstruction>(val)->getOperand(0);
477+
break;
478+
case ValueKind::StructInst: {
479+
auto *SI = cast<StructInst>(val);
480+
if (SI->getNumOperands() != 1)
481+
return nullptr;
482+
val = SI->getOperand(0);
483+
break;
484+
}
485+
case ValueKind::BuiltinInst: {
486+
auto *B = cast<BuiltinInst>(val);
487+
switch (B->getBuiltinInfo().ID) {
488+
case BuiltinValueKind::StringObjectOr:
489+
// Note that it is a requirement that the or'd bits of the left
490+
// operand are initially zero.
491+
if (auto opVal = getIntConst(B->getArguments()[1])) {
492+
setBits |= opVal.getValue();
493+
} else {
494+
return nullptr;
495+
}
496+
LLVM_FALLTHROUGH;
497+
case BuiltinValueKind::ZExtOrBitCast:
498+
case BuiltinValueKind::PtrToInt:
499+
val = B->getArguments()[0];
500+
break;
501+
default:
502+
return nullptr;
503+
}
504+
break;
505+
}
506+
default:
507+
return nullptr;
508+
}
509+
}
510+
return Builder.createIntegerLiteral(BI->getLoc(), BI->getType(),
511+
setBits & andBits);
512+
}
513+
433514
SILInstruction *SILCombiner::visitBuiltinInst(BuiltinInst *I) {
434515
if (I->getBuiltinInfo().ID == BuiltinValueKind::CanBeObjCClass)
435516
return optimizeBuiltinCanBeObjCClass(I);
@@ -496,6 +577,9 @@ SILInstruction *SILCombiner::visitBuiltinInst(BuiltinInst *I) {
496577
break;
497578
}
498579
case BuiltinValueKind::And:
580+
if (SILInstruction *optimized = optimizeStringObject(I))
581+
return optimized;
582+
499583
return optimizeBitOp(I,
500584
[](APInt &left, const APInt &right) { left &= right; } /* combine */,
501585
[](const APInt &i) -> bool { return i.isAllOnesValue(); } /* isNeutral */,

test/SILOptimizer/sil_combine.sil

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3579,3 +3579,61 @@ bb0(%0 : $Builtin.Int64):
35793579
strong_retain %2 : $Builtin.BridgeObject
35803580
return %2 : $Builtin.BridgeObject
35813581
}
3582+
3583+
// CHECK-LABEL: sil @optimize_stringObject_bit_operations
3584+
// CHECK: bb0:
3585+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int64, 4611686018427387904
3586+
// CHECK-NEXT: return %0
3587+
sil @optimize_stringObject_bit_operations : $@convention(thin) () -> Builtin.Int64 {
3588+
bb0:
3589+
%2 = string_literal utf8 "thequickbrownfoxjumpsoverthelazydogusingasmanycharacteraspossible123456789"
3590+
%5 = builtin "ptrtoint_Word"(%2 : $Builtin.RawPointer) : $Builtin.Word
3591+
%6 = builtin "zextOrBitCast_Word_Int64"(%5 : $Builtin.Word) : $Builtin.Int64
3592+
%9 = integer_literal $Builtin.Int64, 13835058055282163712
3593+
%10 = builtin "stringObjectOr_Int64"(%6 : $Builtin.Int64, %9 : $Builtin.Int64) : $Builtin.Int64
3594+
%11 = struct $UInt64 (%10 : $Builtin.Int64)
3595+
%12 = value_to_bridge_object %11 : $UInt64
3596+
%33 = unchecked_trivial_bit_cast %12 : $Builtin.BridgeObject to $UInt64
3597+
%34 = integer_literal $Builtin.Int64, 6917529027641081856
3598+
%35 = struct_extract %33 : $UInt64, #UInt64._value
3599+
%36 = builtin "and_Int64"(%35 : $Builtin.Int64, %34 : $Builtin.Int64) : $Builtin.Int64
3600+
return %36 : $Builtin.Int64
3601+
}
3602+
3603+
// CHECK-LABEL: sil @dont_optimize_stringObject_bit_operations1
3604+
// CHECK: builtin "stringObjectOr_Int64"
3605+
// CHECK: builtin "and_Int64"
3606+
sil @dont_optimize_stringObject_bit_operations1 : $@convention(thin) (Builtin.RawPointer) -> Builtin.Int64 {
3607+
bb0(%2 : $Builtin.RawPointer):
3608+
%5 = builtin "ptrtoint_Word"(%2 : $Builtin.RawPointer) : $Builtin.Word
3609+
%6 = builtin "zextOrBitCast_Word_Int64"(%5 : $Builtin.Word) : $Builtin.Int64
3610+
%9 = integer_literal $Builtin.Int64, -9223372036854775808
3611+
%10 = builtin "stringObjectOr_Int64"(%6 : $Builtin.Int64, %9 : $Builtin.Int64) : $Builtin.Int64
3612+
%11 = struct $UInt64 (%10 : $Builtin.Int64)
3613+
%12 = value_to_bridge_object %11 : $UInt64
3614+
%33 = unchecked_trivial_bit_cast %12 : $Builtin.BridgeObject to $UInt64
3615+
%34 = integer_literal $Builtin.Int64, 4611686018427387904
3616+
%35 = struct_extract %33 : $UInt64, #UInt64._value
3617+
%36 = builtin "and_Int64"(%35 : $Builtin.Int64, %34 : $Builtin.Int64) : $Builtin.Int64
3618+
return %36 : $Builtin.Int64
3619+
}
3620+
3621+
// CHECK-LABEL: sil @dont_optimize_stringObject_bit_operations2
3622+
// CHECK: builtin "stringObjectOr_Int64"
3623+
// CHECK: builtin "and_Int64"
3624+
sil @dont_optimize_stringObject_bit_operations2 : $@convention(thin) () -> Builtin.Int64 {
3625+
bb0:
3626+
%2 = string_literal utf8 "thequickbrownfoxjumpsoverthelazydogusingasmanycharacteraspossible123456789"
3627+
%5 = builtin "ptrtoint_Word"(%2 : $Builtin.RawPointer) : $Builtin.Word
3628+
%6 = builtin "zextOrBitCast_Word_Int64"(%5 : $Builtin.Word) : $Builtin.Int64
3629+
%9 = integer_literal $Builtin.Int64, 13835058055282163712
3630+
%10 = builtin "stringObjectOr_Int64"(%6 : $Builtin.Int64, %9 : $Builtin.Int64) : $Builtin.Int64
3631+
%11 = struct $UInt64 (%10 : $Builtin.Int64)
3632+
%12 = value_to_bridge_object %11 : $UInt64
3633+
%33 = unchecked_trivial_bit_cast %12 : $Builtin.BridgeObject to $UInt64
3634+
%34 = integer_literal $Builtin.Int64, 1152921504606846975
3635+
%35 = struct_extract %33 : $UInt64, #UInt64._value
3636+
%36 = builtin "and_Int64"(%35 : $Builtin.Int64, %34 : $Builtin.Int64) : $Builtin.Int64
3637+
return %36 : $Builtin.Int64
3638+
}
3639+

0 commit comments

Comments
 (0)