diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp --- a/llvm/lib/Transforms/Scalar/SCCP.cpp +++ b/llvm/lib/Transforms/Scalar/SCCP.cpp @@ -173,6 +173,8 @@ DenseMap AnalysisResults; DenseMap> AdditionalUsers; + SmallVector MismatchedSectionArgs; + LLVMContext &Ctx; public: @@ -354,6 +356,11 @@ return nullptr; } + bool IsMismatchedSectionArg(Value *V) { + return std::find(MismatchedSectionArgs.begin(), MismatchedSectionArgs.end(), + V) != MismatchedSectionArgs.end(); + } + private: ConstantInt *getConstantInt(const ValueLatticeElement &IV) const { return dyn_cast_or_null(getConstant(IV)); @@ -1096,6 +1103,11 @@ ConstantExpr::getGetElementPtr(I.getSourceElementType(), Ptr, Indices); if (isa(C)) return; + + if (auto GV = dyn_cast(Ptr)) + if (GV->hasSection() && IsMismatchedSectionArg(I.getPointerOperand())) + return (void)markOverdefined(&I); + markConstant(&I, C); } @@ -1236,6 +1248,12 @@ void SCCPSolver::handleCallArguments(CallBase &CB) { Function *F = CB.getCalledFunction(); + bool SectionMismatch = false; + + if (!isa(CB) && F) + if (F->getSection() != CB.getParent()->getParent()->getSection()) + SectionMismatch = true; + // If this is a local function that doesn't have its address taken, mark its // entry block executable and merge in the actual arguments to the call into // the formal arguments of the function. @@ -1254,6 +1272,9 @@ continue; } + if (SectionMismatch) + MismatchedSectionArgs.push_back(&*AI); + if (auto *STy = dyn_cast(AI->getType())) { for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) { ValueLatticeElement CallArg = getStructValueState(*CAI, i); @@ -1660,6 +1681,17 @@ LLVM_DEBUG(dbgs() << " Constant: " << *Const << " = " << *V << '\n'); + // Now that we've found the constant to replace, check if it's in an explicit + // section, and if we previously found a mismatch between explicit sections + // between the callee whose argument we're currently trying to replace, and + // the caller who passed the constant. + // + // Skip this optimization, as it can lead to references to global values in + // explicit sections from functions with incompatible function sections. + if (auto GV = dyn_cast(Const)) + if (GV->hasSection() && Solver.IsMismatchedSectionArg(V)) + return false; + // Replaces all of the uses of a variable with uses of the constant. V->replaceAllUsesWith(Const); return true; diff --git a/llvm/test/Transforms/SCCP/ipsccp-function-sections.ll b/llvm/test/Transforms/SCCP/ipsccp-function-sections.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/SCCP/ipsccp-function-sections.ll @@ -0,0 +1,79 @@ +; RUN: opt -passes=ipsccp -S -o - %s | FileCheck %s +; load test +@foo = dso_local global i64 zeroinitializer, section ".init.data", align 8 +@bar = dso_local global i64 zeroinitializer, align 8 + +define internal void @quux(i64* %x, i64* %y) { +; The load of %y should be replaced with a load of @bar, because @bar is not in +; any explicit section. +; +; The load of %x should *not* be replaced with a load of @foo, because @foo is +; in an explicit section, and the explicit sections of the caller and callee do +; not match. +; CHECK-LABEL: @quux( +; CHECK-NEXT: %z = load i64, i64* %x, align 4 +; CHECK-NEXT: %w = load i64, i64* @bar, align 4 +; CHECK-NEXT: ret void + + %z = load i64, i64* %x + %w = load i64, i64* %y + ret void +} + +define dso_local void @baz() section ".init.text" { + call void @quux(i64* @foo, i64* @bar) + ret void +} + +; store test +@g1 = dso_local global i64 zeroinitializer, section ".init.data", align 8 +@g2 = dso_local global i64 zeroinitializer, align 8 + +define internal void @g3(i64* %x, i64* %y) { +; The store of %y should be replaced with a store of @g2, because @g2 is not in +; any explicit section. +; +; The store of %x should *not* be replaced with a store of @g1, because @g1 is +; in an explicit section, and the explicit sections of the caller and callee do +; not match. +; CHECK-LABEL: @g3( +; CHECK-NEXT: store i64 42, i64* %x, align 4 +; CHECK-NEXT: store i64 42, i64* @g2, align 4 +; CHECK-NEXT: ret void + store i64 42, i64* %x + store i64 42, i64* %y + ret void +} +define dso_local void @g4() section ".init.text" { + call void @g3(i64* @g1, i64* @g2) + ret void +} + +; GEP test +%struct.e = type { i64 } +@numa_nodes_parsed = dso_local global %struct.e zeroinitializer, section ".init.data", align 8 +@numa_nodes_parsed2 = dso_local global %struct.e zeroinitializer, align 8 + +define internal i64 @__nodes_weight(%struct.e* %g, %struct.e* %h) section ".init.text" { +; The GEP of %g should not be replaced with a GEP of @numa_nodes_parsed. +; CHECK-LABEL: @__nodes_weight( +; CHECK-NEXT: %bits = getelementptr inbounds %struct.e, %struct.e* %g, i32 0, i32 0 +; CHECK-NEXT: %x = load i64, i64* %bits, align 8 +; CHECK-NEXT: %x2 = load i64, i64* getelementptr inbounds (%struct.e, %struct.e* @numa_nodes_parsed2, i32 0, i32 0), align 8 +; CHECK-NEXT: ret i64 %x + + %bits = getelementptr inbounds %struct.e, %struct.e* %g, i32 0, i32 0 + %x = load i64, i64* %bits, align 8 + %bits2 = getelementptr inbounds %struct.e, %struct.e* %h, i32 0, i32 0 + %x2 = load i64, i64* %bits2, align 8 + ret i64 %x +} + +define dso_local void @amd_numa_init() { +; CHECK-LABEL: @amd_numa_init( +; CHECK-NEXT: %call6 = call i64 @__nodes_weight(%struct.e* @numa_nodes_parsed, %struct.e* @numa_nodes_parsed2) +; CHECK-NEXT: ret void +; + %call6 = call i64 @__nodes_weight(%struct.e* @numa_nodes_parsed, %struct.e* @numa_nodes_parsed2) + ret void +}