Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1013,9 +1013,10 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
if (IsThinLTOPostLink)
PB.registerPipelineStartEPCallback(
[](ModulePassManager &MPM, OptimizationLevel Level) {
MPM.addPass(LowerTypeTestsPass(/*ExportSummary=*/nullptr,
/*ImportSummary=*/nullptr,
/*DropTypeTests=*/true));
MPM.addPass(LowerTypeTestsPass(
/*ExportSummary=*/nullptr,
/*ImportSummary=*/nullptr,
/*DropTypeTests=*/lowertypetests::DropTestKind::Assume));
});

// Register callbacks to schedule sanitizer passes at the appropriate part
Expand Down
46 changes: 46 additions & 0 deletions clang/test/CodeGen/fat-lto-objects-cfi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// REQUIRES: x86-registered-target

// RUN: %clangxx --target=x86_64-unknown-fuchsia -O2 -flto -ffat-lto-objects \
// RUN: -fsanitize=cfi -fvisibility=hidden -S -emit-llvm -o - %s \
// RUN: | FileCheck %s

// CHECK: llvm.embedded.object
// CHECK-SAME: section ".llvm.lto"

// CHECK-LABEL: define hidden void @foo
// CHECK: entry:
// CHECK-NEXT: %cmp14.not = icmp eq i64 %len, 0
// CHECK-NEXT: br i1 %cmp14.not, label %for.end7, label %for.cond1.preheader.preheader
// CHECK: for.cond1.preheader.preheader: ; preds = %entry
// CHECK-NEXT: %arrayidx.1 = getelementptr inbounds nuw i8, ptr %ptr, i64 4
// CHECK-NEXT: br label %for.cond1.preheader

// CHECK-NOT: @llvm.type.test

// The code below is a reduced case from https://github.com/llvm/llvm-project/issues/112053
#define __PRINTFLIKE(__fmt, __varargs) __attribute__((__format__(__printf__, __fmt, __varargs)))
typedef void func(void* arg, const char* fmt, ...) __PRINTFLIKE(2, 3);
typedef __SIZE_TYPE__ size_t;
typedef unsigned long uintptr_t;

extern "C"
void foo(const void* ptr, size_t len, long disp_addr,
func* printf_func, void* printf_arg) {
uintptr_t address = (uintptr_t)ptr;
size_t count;

for (count = 0; count < len; count += 16) {
union {
unsigned int buf[4];
unsigned char cbuf[16];
} u;
size_t s = 10;
size_t i;

for (i = 0; i < s / 4; i++) {
u.buf[i] = ((const unsigned int*)address)[i];
printf_func(printf_arg, "%08x ", static_cast<unsigned int>(u.buf[i]));
}
}
}

13 changes: 11 additions & 2 deletions llvm/include/llvm/Transforms/IPO/LowerTypeTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,20 +195,29 @@ struct ByteArrayBuilder {

bool isJumpTableCanonical(Function *F);

/// Specifies how to drop type tests.
enum class DropTestKind {
None, /// Do not drop type tests (default).
Assume, /// Drop only llvm.assumes using type test value.
All, /// Drop the type test and all uses.
};

} // end namespace lowertypetests

class LowerTypeTestsPass : public PassInfoMixin<LowerTypeTestsPass> {
bool UseCommandLine = false;

ModuleSummaryIndex *ExportSummary = nullptr;
const ModuleSummaryIndex *ImportSummary = nullptr;
bool DropTypeTests = true;
lowertypetests::DropTestKind DropTypeTests =
lowertypetests::DropTestKind::None;

public:
LowerTypeTestsPass() : UseCommandLine(true) {}
LowerTypeTestsPass(ModuleSummaryIndex *ExportSummary,
const ModuleSummaryIndex *ImportSummary,
bool DropTypeTests = false)
lowertypetests::DropTestKind DropTypeTests =
lowertypetests::DropTestKind::None)
: ExportSummary(ExportSummary), ImportSummary(ImportSummary),
DropTypeTests(DropTypeTests) {}
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
Expand Down
22 changes: 17 additions & 5 deletions llvm/lib/Passes/PassBuilderPipelines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,8 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
// post link pipeline after ICP. This is to enable usage of the type
// tests in ICP sequences.
if (Phase == ThinOrFullLTOPhase::ThinLTOPostLink)
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr, true));
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr,
lowertypetests::DropTestKind::Assume));

invokePipelineEarlySimplificationEPCallbacks(MPM, Level);

Expand Down Expand Up @@ -1644,6 +1645,13 @@ PassBuilder::buildFatLTODefaultPipeline(OptimizationLevel Level, bool ThinLTO,
MPM.addPass(buildLTOPreLinkDefaultPipeline(Level));
MPM.addPass(EmbedBitcodePass(ThinLTO, EmitSummary));

// If we're doing FatLTO w/ CFI enabled, we don't want the type tests in the
// object code, only in the bitcode section, so drop it before we run
// module optimization and generate machine code. If llvm.type.test() isn't in
// the IR, this won't do anything.
MPM.addPass(
LowerTypeTestsPass(nullptr, nullptr, lowertypetests::DropTestKind::All));

// Use the ThinLTO post-link pipeline with sample profiling
if (ThinLTO && PGOOpt && PGOOpt->Action == PGOOptions::SampleUse)
MPM.addPass(buildThinLTODefaultPipeline(Level, /*ImportSummary=*/nullptr));
Expand Down Expand Up @@ -1750,7 +1758,8 @@ ModulePassManager PassBuilder::buildThinLTODefaultPipeline(
if (Level == OptimizationLevel::O0) {
// Run a second time to clean up any type tests left behind by WPD for use
// in ICP.
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr, true));
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr,
lowertypetests::DropTestKind::Assume));
// Drop available_externally and unreferenced globals. This is necessary
// with ThinLTO in order to avoid leaving undefined references to dead
// globals in the object file.
Expand Down Expand Up @@ -1801,7 +1810,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
MPM.addPass(LowerTypeTestsPass(ExportSummary, nullptr));
// Run a second time to clean up any type tests left behind by WPD for use
// in ICP.
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr, true));
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr,
lowertypetests::DropTestKind::Assume));

invokeFullLinkTimeOptimizationLastEPCallbacks(MPM, Level);

Expand Down Expand Up @@ -1879,7 +1889,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
// Run a second time to clean up any type tests left behind by WPD for use
// in ICP (which is performed earlier than this in the regular LTO
// pipeline).
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr, true));
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr,
lowertypetests::DropTestKind::Assume));

invokeFullLinkTimeOptimizationLastEPCallbacks(MPM, Level);

Expand Down Expand Up @@ -2060,7 +2071,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
MPM.addPass(LowerTypeTestsPass(ExportSummary, nullptr));
// Run a second time to clean up any type tests left behind by WPD for use
// in ICP (which is performed earlier than this in the regular LTO pipeline).
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr, true));
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr,
lowertypetests::DropTestKind::Assume));

// Enable splitting late in the FullLTO post-link pipeline.
if (EnableHotColdSplit)
Expand Down
41 changes: 27 additions & 14 deletions llvm/lib/Transforms/IPO/LowerTypeTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,16 @@ static cl::opt<std::string> ClWriteSummary(
cl::desc("Write summary to given YAML file after running pass"),
cl::Hidden);

static cl::opt<bool>
static cl::opt<DropTestKind>
ClDropTypeTests("lowertypetests-drop-type-tests",
cl::desc("Simply drop type test assume sequences"),
cl::Hidden, cl::init(false));
cl::desc("Simply drop type test sequences"),
cl::values(clEnumValN(DropTestKind::None, "none",
"Do not drop any type tests"),
clEnumValN(DropTestKind::Assume, "assume",
"Drop type test assume sequences"),
clEnumValN(DropTestKind::All, "all",
"Drop all type test sequences")),
cl::Hidden, cl::init(DropTestKind::None));

bool BitSetInfo::containsGlobalOffset(uint64_t Offset) const {
if (Offset < ByteOffset)
Expand Down Expand Up @@ -399,7 +405,7 @@ class LowerTypeTestsModule {
const ModuleSummaryIndex *ImportSummary;
// Set when the client has invoked this to simply drop all type test assume
// sequences.
bool DropTypeTests;
DropTestKind DropTypeTests;

Triple::ArchType Arch;
Triple::OSType OS;
Expand Down Expand Up @@ -542,7 +548,7 @@ class LowerTypeTestsModule {
LowerTypeTestsModule(Module &M, ModuleAnalysisManager &AM,
ModuleSummaryIndex *ExportSummary,
const ModuleSummaryIndex *ImportSummary,
bool DropTypeTests);
DropTestKind DropTypeTests);

bool lower();

Expand Down Expand Up @@ -1828,9 +1834,10 @@ void LowerTypeTestsModule::buildBitSetsFromDisjointSet(
/// Lower all type tests in this module.
LowerTypeTestsModule::LowerTypeTestsModule(
Module &M, ModuleAnalysisManager &AM, ModuleSummaryIndex *ExportSummary,
const ModuleSummaryIndex *ImportSummary, bool DropTypeTests)
const ModuleSummaryIndex *ImportSummary, DropTestKind DropTypeTests)
: M(M), ExportSummary(ExportSummary), ImportSummary(ImportSummary),
DropTypeTests(DropTypeTests || ClDropTypeTests) {
DropTypeTests(ClDropTypeTests > DropTypeTests ? ClDropTypeTests
: DropTypeTests) {
assert(!(ExportSummary && ImportSummary));
Triple TargetTriple(M.getTargetTriple());
Arch = TargetTriple.getArch();
Expand Down Expand Up @@ -1882,7 +1889,7 @@ bool LowerTypeTestsModule::runForTesting(Module &M, ModuleAnalysisManager &AM) {
M, AM,
ClSummaryAction == PassSummaryAction::Export ? &Summary : nullptr,
ClSummaryAction == PassSummaryAction::Import ? &Summary : nullptr,
/*DropTypeTests*/ false)
/*DropTypeTests=*/DropTestKind::None)
.lower();

if (!ClWriteSummary.empty()) {
Expand Down Expand Up @@ -1949,7 +1956,8 @@ void LowerTypeTestsModule::replaceDirectCalls(Value *Old, Value *New) {
Old->replaceUsesWithIf(New, isDirectCall);
}

static void dropTypeTests(Module &M, Function &TypeTestFunc) {
static void dropTypeTests(Module &M, Function &TypeTestFunc,
bool ShouldDropAll) {
for (Use &U : llvm::make_early_inc_range(TypeTestFunc.uses())) {
auto *CI = cast<CallInst>(U.getUser());
// Find and erase llvm.assume intrinsics for this llvm.type.test call.
Expand All @@ -1959,9 +1967,13 @@ static void dropTypeTests(Module &M, Function &TypeTestFunc) {
// If the assume was merged with another assume, we might have a use on a
// phi (which will feed the assume). Simply replace the use on the phi
// with "true" and leave the merged assume.
//
// If ShouldDropAll is set, then we we need to update any remaining uses,
// regardless of the instruction type.
if (!CI->use_empty()) {
assert(
all_of(CI->users(), [](User *U) -> bool { return isa<PHINode>(U); }));
assert(ShouldDropAll || all_of(CI->users(), [](User *U) -> bool {
return isa<PHINode>(U);
}));
CI->replaceAllUsesWith(ConstantInt::getTrue(M.getContext()));
}
CI->eraseFromParent();
Expand All @@ -1972,16 +1984,17 @@ bool LowerTypeTestsModule::lower() {
Function *TypeTestFunc =
Intrinsic::getDeclarationIfExists(&M, Intrinsic::type_test);

if (DropTypeTests) {
if (DropTypeTests != DropTestKind::None) {
bool ShouldDropAll = DropTypeTests == DropTestKind::All;
if (TypeTestFunc)
dropTypeTests(M, *TypeTestFunc);
dropTypeTests(M, *TypeTestFunc, ShouldDropAll);
// Normally we'd have already removed all @llvm.public.type.test calls,
// except for in the case where we originally were performing ThinLTO but
// decided not to in the backend.
Function *PublicTypeTestFunc =
Intrinsic::getDeclarationIfExists(&M, Intrinsic::public_type_test);
if (PublicTypeTestFunc)
dropTypeTests(M, *PublicTypeTestFunc);
dropTypeTests(M, *PublicTypeTestFunc, ShouldDropAll);
if (TypeTestFunc || PublicTypeTestFunc) {
// We have deleted the type intrinsics, so we no longer have enough
// information to reason about the liveness of virtual function pointers
Expand Down
22 changes: 22 additions & 0 deletions llvm/test/Transforms/LowerTypeTests/drop_type_test.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
; RUN: opt -S -passes=lowertypetests -lowertypetests-drop-type-tests=all < %s | FileCheck %s

define void @func() {
entry:
%0 = tail call i1 @llvm.type.test(ptr null, metadata !"foo")
br i1 %0, label %exit, label %trap

trap:
unreachable

exit:
ret void
; CHECK-LABEL: entry:
; CHECK-NEXT: br i1 true, label %exit, label %trap
; CHECK-LABEL: trap:
; CHECK-NEXT: unreachable
; CHECK-LABEL: exit:
; CHECK-NEXT: ret void
}

declare i1 @llvm.type.test(ptr, metadata) #0
attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
2 changes: 1 addition & 1 deletion llvm/test/Transforms/LowerTypeTests/drop_type_test_phi.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
; Test to ensure dropping of type tests can handle a phi feeding the assume.
; RUN: opt -S -passes=lowertypetests -lowertypetests-drop-type-tests -mtriple=x86_64-unknown-linux-gnu %s | FileCheck %s
; RUN: opt -S -passes=lowertypetests -lowertypetests-drop-type-tests=assume -mtriple=x86_64-unknown-linux-gnu %s | FileCheck %s

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-grtev4-linux-gnu"
Expand Down
Loading