Revert "[LLVM] Add IRNormalizer Pass" (#113392)
Reverts llvm/llvm-project#68176 Introduced BuildBot failure: https://github.com/llvm/llvm-project/pull/68176#issuecomment-2428243474
This commit is contained in:
@@ -543,14 +543,6 @@ variables with initializers are marked as internal.
|
||||
An interprocedural variant of :ref:`Sparse Conditional Constant Propagation
|
||||
<passes-sccp>`.
|
||||
|
||||
``ir-normalizer``: Transforms IR into a normal form that's easier to diff
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
This pass aims to transform LLVM Modules into a normal form by reordering and
|
||||
renaming instructions while preserving the same semantics. The normalizer makes
|
||||
it easier to spot semantic differences while diffing two modules which have
|
||||
undergone two different passes.
|
||||
|
||||
``jump-threading``: Jump Threading
|
||||
----------------------------------
|
||||
|
||||
|
||||
@@ -42,11 +42,6 @@ point (e.g. maybe you would like to give an example of the
|
||||
functionality, or simply have a lot to talk about), see the comment below
|
||||
for adding a new subsection. -->
|
||||
|
||||
* Added a new IRNormalizer pass which aims to transform LLVM modules into
|
||||
a normal form by reordering and renaming instructions while preserving the
|
||||
same semantics. The normalizer makes it easier to spot semantic differences
|
||||
when diffing two modules which have undergone different passes.
|
||||
|
||||
* ...
|
||||
|
||||
<!-- If you would like to document a larger change, then you can add a
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef LLVM_TRANSFORMS_UTILS_IRNORMALIZER_H
|
||||
#define LLVM_TRANSFORMS_UTILS_IRNORMALIZER_H
|
||||
|
||||
#include "llvm/IR/PassManager.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// IRNormalizer aims to transform LLVM IR into normal form.
|
||||
struct IRNormalizerPass : public PassInfoMixin<IRNormalizerPass> {
|
||||
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) const;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_TRANSFORMS_UTILS_IRNORMALIZER_H
|
||||
@@ -303,7 +303,6 @@
|
||||
#include "llvm/Transforms/Utils/EntryExitInstrumenter.h"
|
||||
#include "llvm/Transforms/Utils/FixIrreducible.h"
|
||||
#include "llvm/Transforms/Utils/HelloWorld.h"
|
||||
#include "llvm/Transforms/Utils/IRNormalizer.h"
|
||||
#include "llvm/Transforms/Utils/InjectTLIMappings.h"
|
||||
#include "llvm/Transforms/Utils/InstructionNamer.h"
|
||||
#include "llvm/Transforms/Utils/Instrumentation.h"
|
||||
|
||||
@@ -412,7 +412,6 @@ FUNCTION_PASS("move-auto-init", MoveAutoInitPass())
|
||||
FUNCTION_PASS("nary-reassociate", NaryReassociatePass())
|
||||
FUNCTION_PASS("newgvn", NewGVNPass())
|
||||
FUNCTION_PASS("no-op-function", NoOpFunctionPass())
|
||||
FUNCTION_PASS("normalize", IRNormalizerPass())
|
||||
FUNCTION_PASS("objc-arc", ObjCARCOptPass())
|
||||
FUNCTION_PASS("objc-arc-contract", ObjCARCContractPass())
|
||||
FUNCTION_PASS("objc-arc-expand", ObjCARCExpandPass())
|
||||
|
||||
@@ -37,7 +37,6 @@ add_llvm_component_library(LLVMTransformUtils
|
||||
InstructionNamer.cpp
|
||||
Instrumentation.cpp
|
||||
IntegerDivision.cpp
|
||||
IRNormalizer.cpp
|
||||
LCSSA.cpp
|
||||
LibCallsShrinkWrap.cpp
|
||||
Local.cpp
|
||||
|
||||
@@ -1,695 +0,0 @@
|
||||
//===--------------- IRNormalizer.cpp - IR Normalizer ---------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// \file
|
||||
/// This file implements the IRNormalizer class which aims to transform LLVM
|
||||
/// Modules into a normal form by reordering and renaming instructions while
|
||||
/// preserving the same semantics. The normalizer makes it easier to spot
|
||||
/// semantic differences while diffing two modules which have undergone
|
||||
/// different passes.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Transforms/Utils/IRNormalizer.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/InitializePasses.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/PassRegistry.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Transforms/Utils.h"
|
||||
#include <algorithm>
|
||||
#include <stack>
|
||||
|
||||
#define DEBUG_TYPE "normalize"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
/// IRNormalizer aims to transform LLVM IR into normal form.
|
||||
class IRNormalizer {
|
||||
public:
|
||||
/// \name Normalizer flags.
|
||||
/// @{
|
||||
/// Preserves original order of instructions.
|
||||
static cl::opt<bool> PreserveOrder;
|
||||
/// Renames all instructions (including user-named).
|
||||
static cl::opt<bool> RenameAll; // TODO: Don't rename on empty name
|
||||
/// Folds all regular instructions (including pre-outputs).
|
||||
static cl::opt<bool> FoldPreOutputs;
|
||||
/// Sorts and reorders operands in commutative instructions.
|
||||
static cl::opt<bool> ReorderOperands;
|
||||
/// @}
|
||||
|
||||
bool runOnFunction(Function &F);
|
||||
|
||||
private:
|
||||
// Random constant for hashing, so the state isn't zero.
|
||||
const uint64_t MagicHashConstant = 0x6acaa36bef8325c5ULL;
|
||||
DenseSet<const Instruction *> NamedInstructions;
|
||||
|
||||
SmallVector<Instruction *, 16> Outputs;
|
||||
|
||||
/// \name Naming.
|
||||
/// @{
|
||||
void nameFunctionArguments(Function &F) const;
|
||||
void nameBasicBlocks(Function &F) const;
|
||||
void nameInstruction(Instruction *I);
|
||||
void nameAsInitialInstruction(Instruction *I) const;
|
||||
void nameAsRegularInstruction(Instruction *I);
|
||||
void foldInstructionName(Instruction *I) const;
|
||||
/// @}
|
||||
|
||||
/// \name Reordering.
|
||||
/// @{
|
||||
void reorderInstructions(Function &F) const;
|
||||
void reorderDefinition(Instruction *Definition,
|
||||
std::stack<Instruction *> &TopologicalSort,
|
||||
SmallPtrSet<const Instruction *, 32> &Visited) const;
|
||||
void reorderInstructionOperandsByNames(Instruction *I) const;
|
||||
void reorderPHIIncomingValues(PHINode *Phi) const;
|
||||
/// @}
|
||||
|
||||
/// \name Utility methods.
|
||||
/// @{
|
||||
template <typename T>
|
||||
void sortCommutativeOperands(Instruction *I, T &Operands) const;
|
||||
SmallVector<Instruction *, 16> collectOutputInstructions(Function &F) const;
|
||||
bool isOutput(const Instruction *I) const;
|
||||
bool isInitialInstruction(const Instruction *I) const;
|
||||
bool hasOnlyImmediateOperands(const Instruction *I) const;
|
||||
SetVector<int>
|
||||
getOutputFootprint(Instruction *I,
|
||||
SmallPtrSet<const Instruction *, 32> &Visited) const;
|
||||
/// @}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
cl::opt<bool> IRNormalizer::PreserveOrder(
|
||||
"norm-preserve-order", cl::Hidden, cl::init(false),
|
||||
cl::desc("Preserves original instruction order"));
|
||||
cl::opt<bool> IRNormalizer::RenameAll(
|
||||
"norm-rename-all", cl::Hidden, cl::init(true),
|
||||
cl::desc("Renames all instructions (including user-named)"));
|
||||
cl::opt<bool> IRNormalizer::FoldPreOutputs(
|
||||
"norm-fold-all", cl::Hidden, cl::init(true),
|
||||
cl::desc("Folds all regular instructions (including pre-outputs)"));
|
||||
cl::opt<bool> IRNormalizer::ReorderOperands(
|
||||
"norm-reorder-operands", cl::Hidden, cl::init(true),
|
||||
cl::desc("Sorts and reorders operands in commutative instructions"));
|
||||
|
||||
/// Entry method to the IRNormalizer.
|
||||
///
|
||||
/// \param F Function to normalize.
|
||||
bool IRNormalizer::runOnFunction(Function &F) {
|
||||
nameFunctionArguments(F);
|
||||
nameBasicBlocks(F);
|
||||
|
||||
Outputs = collectOutputInstructions(F);
|
||||
|
||||
if (!PreserveOrder)
|
||||
reorderInstructions(F);
|
||||
|
||||
// TODO: Reorder basic blocks via a topological sort.
|
||||
|
||||
for (auto &I : Outputs)
|
||||
nameInstruction(I);
|
||||
|
||||
for (auto &I : instructions(F)) {
|
||||
if (!PreserveOrder) {
|
||||
if (ReorderOperands)
|
||||
reorderInstructionOperandsByNames(&I);
|
||||
|
||||
if (auto *Phi = dyn_cast<PHINode>(&I))
|
||||
reorderPHIIncomingValues(Phi);
|
||||
}
|
||||
foldInstructionName(&I);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Numbers arguments.
|
||||
///
|
||||
/// \param F Function whose arguments will be renamed.
|
||||
void IRNormalizer::nameFunctionArguments(Function &F) const {
|
||||
int ArgumentCounter = 0;
|
||||
for (auto &A : F.args()) {
|
||||
if (RenameAll || A.getName().empty()) {
|
||||
A.setName("a" + Twine(ArgumentCounter));
|
||||
ArgumentCounter += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Names basic blocks using a generated hash for each basic block in
|
||||
/// a function considering the opcode and the order of output instructions.
|
||||
///
|
||||
/// \param F Function containing basic blocks to rename.
|
||||
void IRNormalizer::nameBasicBlocks(Function &F) const {
|
||||
for (auto &B : F) {
|
||||
// Initialize to a magic constant, so the state isn't zero.
|
||||
uint64_t Hash = MagicHashConstant;
|
||||
|
||||
// Hash considering output instruction opcodes.
|
||||
for (auto &I : B)
|
||||
if (isOutput(&I))
|
||||
Hash = hashing::detail::hash_16_bytes(Hash, I.getOpcode());
|
||||
|
||||
if (RenameAll || B.getName().empty()) {
|
||||
// Name basic block. Substring hash to make diffs more readable.
|
||||
B.setName("bb" + std::to_string(Hash).substr(0, 5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Names instructions graphically (recursive) in accordance with the
|
||||
/// def-use tree, starting from the initial instructions (defs), finishing at
|
||||
/// the output (top-most user) instructions (depth-first).
|
||||
///
|
||||
/// \param I Instruction to be renamed.
|
||||
void IRNormalizer::nameInstruction(Instruction *I) {
|
||||
// Ensure instructions are not renamed. This is done
|
||||
// to prevent situation where instructions are used
|
||||
// before their definition (in phi nodes)
|
||||
if (NamedInstructions.contains(I))
|
||||
return;
|
||||
NamedInstructions.insert(I);
|
||||
if (isInitialInstruction(I)) {
|
||||
nameAsInitialInstruction(I);
|
||||
} else {
|
||||
// This must be a regular instruction.
|
||||
nameAsRegularInstruction(I);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void IRNormalizer::sortCommutativeOperands(Instruction *I, T &Operands) const {
|
||||
if (!(I->isCommutative() && Operands.size() >= 2))
|
||||
return;
|
||||
auto CommutativeEnd = Operands.begin();
|
||||
std::advance(CommutativeEnd, 2);
|
||||
llvm::sort(Operands.begin(), CommutativeEnd);
|
||||
}
|
||||
|
||||
/// Names instruction following the scheme:
|
||||
/// vl00000Callee(Operands)
|
||||
///
|
||||
/// Where 00000 is a hash calculated considering instruction's opcode and output
|
||||
/// footprint. Callee's name is only included when instruction's type is
|
||||
/// CallInst. In cases where instruction is commutative, operands list is also
|
||||
/// sorted.
|
||||
///
|
||||
/// Renames instruction only when RenameAll flag is raised or instruction is
|
||||
/// unnamed.
|
||||
///
|
||||
/// \see getOutputFootprint()
|
||||
/// \param I Instruction to be renamed.
|
||||
void IRNormalizer::nameAsInitialInstruction(Instruction *I) const {
|
||||
if (I->getType()->isVoidTy())
|
||||
return;
|
||||
if (!(I->getName().empty() || RenameAll))
|
||||
return;
|
||||
LLVM_DEBUG(dbgs() << "Naming initial instruction: " << *I << "\n");
|
||||
|
||||
// Instruction operands for further sorting.
|
||||
SmallVector<SmallString<64>, 4> Operands;
|
||||
|
||||
// Collect operands.
|
||||
for (auto &Op : I->operands()) {
|
||||
if (!isa<Function>(Op)) {
|
||||
std::string TextRepresentation;
|
||||
raw_string_ostream Stream(TextRepresentation);
|
||||
Op->printAsOperand(Stream, false);
|
||||
Operands.push_back(StringRef(Stream.str()));
|
||||
}
|
||||
}
|
||||
|
||||
sortCommutativeOperands(I, Operands);
|
||||
|
||||
// Initialize to a magic constant, so the state isn't zero.
|
||||
uint64_t Hash = MagicHashConstant;
|
||||
|
||||
// Consider instruction's opcode in the hash.
|
||||
Hash = hashing::detail::hash_16_bytes(Hash, I->getOpcode());
|
||||
|
||||
SmallPtrSet<const Instruction *, 32> Visited;
|
||||
// Get output footprint for I.
|
||||
SetVector<int> OutputFootprint = getOutputFootprint(I, Visited);
|
||||
|
||||
// Consider output footprint in the hash.
|
||||
for (const int &Output : OutputFootprint)
|
||||
Hash = hashing::detail::hash_16_bytes(Hash, Output);
|
||||
|
||||
// Base instruction name.
|
||||
SmallString<256> Name;
|
||||
Name.append("vl" + std::to_string(Hash).substr(0, 5));
|
||||
|
||||
// In case of CallInst, consider callee in the instruction name.
|
||||
if (const auto *CI = dyn_cast<CallInst>(I)) {
|
||||
Function *F = CI->getCalledFunction();
|
||||
|
||||
if (F != nullptr)
|
||||
Name.append(F->getName());
|
||||
}
|
||||
|
||||
Name.append("(");
|
||||
for (size_t i = 0; i < Operands.size(); ++i) {
|
||||
Name.append(Operands[i]);
|
||||
|
||||
if (i < Operands.size() - 1)
|
||||
Name.append(", ");
|
||||
}
|
||||
Name.append(")");
|
||||
|
||||
I->setName(Name);
|
||||
}
|
||||
|
||||
/// Names instruction following the scheme:
|
||||
/// op00000Callee(Operands)
|
||||
///
|
||||
/// Where 00000 is a hash calculated considering instruction's opcode, its
|
||||
/// operands' opcodes and order. Callee's name is only included when
|
||||
/// instruction's type is CallInst. In cases where instruction is commutative,
|
||||
/// operand list is also sorted.
|
||||
///
|
||||
/// Names instructions recursively in accordance with the def-use tree,
|
||||
/// starting from the initial instructions (defs), finishing at
|
||||
/// the output (top-most user) instructions (depth-first).
|
||||
///
|
||||
/// Renames instruction only when RenameAll flag is raised or instruction is
|
||||
/// unnamed.
|
||||
///
|
||||
/// \see getOutputFootprint()
|
||||
/// \param I Instruction to be renamed.
|
||||
void IRNormalizer::nameAsRegularInstruction(Instruction *I) {
|
||||
LLVM_DEBUG(dbgs() << "Naming regular instruction: " << *I << "\n");
|
||||
|
||||
// Instruction operands for further sorting.
|
||||
SmallVector<SmallString<128>, 4> Operands;
|
||||
|
||||
// The name of a regular instruction depends
|
||||
// on the names of its operands. Hence, all
|
||||
// operands must be named first in the use-def
|
||||
// walk.
|
||||
|
||||
// Collect operands.
|
||||
for (auto &Op : I->operands()) {
|
||||
if (auto *I = dyn_cast<Instruction>(Op)) {
|
||||
// Walk down the use-def chain.
|
||||
nameInstruction(I);
|
||||
Operands.push_back(I->getName());
|
||||
} else if (!isa<Function>(Op)) {
|
||||
// This must be an immediate value.
|
||||
std::string TextRepresentation;
|
||||
raw_string_ostream Stream(TextRepresentation);
|
||||
Op->printAsOperand(Stream, false);
|
||||
Operands.push_back(StringRef(Stream.str()));
|
||||
}
|
||||
}
|
||||
|
||||
sortCommutativeOperands(I, Operands);
|
||||
|
||||
// Initialize to a magic constant, so the state isn't zero.
|
||||
uint64_t Hash = MagicHashConstant;
|
||||
|
||||
// Consider instruction opcode in the hash.
|
||||
Hash = hashing::detail::hash_16_bytes(Hash, I->getOpcode());
|
||||
|
||||
// Operand opcodes for further sorting (commutative).
|
||||
SmallVector<int, 4> OperandsOpcodes;
|
||||
|
||||
// Collect operand opcodes for hashing.
|
||||
for (auto &Op : I->operands())
|
||||
if (auto *I = dyn_cast<Instruction>(Op))
|
||||
OperandsOpcodes.push_back(I->getOpcode());
|
||||
|
||||
sortCommutativeOperands(I, OperandsOpcodes);
|
||||
|
||||
// Consider operand opcodes in the hash.
|
||||
for (const int Code : OperandsOpcodes)
|
||||
Hash = hashing::detail::hash_16_bytes(Hash, Code);
|
||||
|
||||
// Base instruction name.
|
||||
SmallString<512> Name;
|
||||
Name.append("op" + std::to_string(Hash).substr(0, 5));
|
||||
|
||||
// In case of CallInst, consider callee in the instruction name.
|
||||
if (const auto *CI = dyn_cast<CallInst>(I))
|
||||
if (const Function *F = CI->getCalledFunction())
|
||||
Name.append(F->getName());
|
||||
|
||||
Name.append("(");
|
||||
for (size_t i = 0; i < Operands.size(); ++i) {
|
||||
Name.append(Operands[i]);
|
||||
|
||||
if (i < Operands.size() - 1)
|
||||
Name.append(", ");
|
||||
}
|
||||
Name.append(")");
|
||||
|
||||
if ((I->getName().empty() || RenameAll) && !I->getType()->isVoidTy())
|
||||
I->setName(Name);
|
||||
}
|
||||
|
||||
/// Shortens instruction's name. This method removes called function name from
|
||||
/// the instruction name and substitutes the call chain with a corresponding
|
||||
/// list of operands.
|
||||
///
|
||||
/// Examples:
|
||||
/// op00000Callee(op00001Callee(...), vl00000Callee(1, 2), ...) ->
|
||||
/// op00000(op00001, vl00000, ...) vl00000Callee(1, 2) -> vl00000(1, 2)
|
||||
///
|
||||
/// This method omits output instructions and pre-output (instructions directly
|
||||
/// used by an output instruction) instructions (by default). By default it also
|
||||
/// does not affect user named instructions.
|
||||
///
|
||||
/// \param I Instruction whose name will be folded.
|
||||
void IRNormalizer::foldInstructionName(Instruction *I) const {
|
||||
// If this flag is raised, fold all regular
|
||||
// instructions (including pre-outputs).
|
||||
if (!FoldPreOutputs) {
|
||||
// Don't fold if one of the users is an output instruction.
|
||||
for (auto *U : I->users())
|
||||
if (auto *IU = dyn_cast<Instruction>(U))
|
||||
if (isOutput(IU))
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't fold if it is an output instruction or has no op prefix.
|
||||
if (isOutput(I) || I->getName().substr(0, 2) != "op")
|
||||
return;
|
||||
|
||||
// Instruction operands.
|
||||
SmallVector<SmallString<64>, 4> Operands;
|
||||
|
||||
for (auto &Op : I->operands()) {
|
||||
if (const auto *I = dyn_cast<Instruction>(Op)) {
|
||||
bool HasNormalName = I->getName().substr(0, 2) == "op" ||
|
||||
I->getName().substr(0, 2) == "vl";
|
||||
|
||||
Operands.push_back(HasNormalName ? I->getName().substr(0, 7)
|
||||
: I->getName());
|
||||
}
|
||||
}
|
||||
|
||||
sortCommutativeOperands(I, Operands);
|
||||
|
||||
SmallString<256> Name;
|
||||
Name.append(I->getName().substr(0, 7));
|
||||
|
||||
Name.append("(");
|
||||
for (size_t i = 0; i < Operands.size(); ++i) {
|
||||
Name.append(Operands[i]);
|
||||
|
||||
if (i < Operands.size() - 1)
|
||||
Name.append(", ");
|
||||
}
|
||||
Name.append(")");
|
||||
|
||||
I->setName(Name);
|
||||
}
|
||||
|
||||
/// Reorders instructions by walking up the tree from each operand of an output
|
||||
/// instruction and reducing the def-use distance.
|
||||
/// This method assumes that output instructions were collected top-down,
|
||||
/// otherwise the def-use chain may be broken.
|
||||
/// This method is a wrapper for recursive reorderInstruction().
|
||||
///
|
||||
/// \see reorderInstruction()
|
||||
void IRNormalizer::reorderInstructions(Function &F) const {
|
||||
for (auto &BB : F) {
|
||||
LLVM_DEBUG(dbgs() << "Reordering instructions in basic block: "
|
||||
<< BB.getName() << "\n");
|
||||
// Find the source nodes of the DAG of instructions in this basic block.
|
||||
// Source nodes are instructions that have side effects, are terminators, or
|
||||
// don't have a parent in the DAG of instructions.
|
||||
//
|
||||
// We must iterate from the first to the last instruction otherwise side
|
||||
// effecting instructions could be reordered.
|
||||
|
||||
std::stack<Instruction *> TopologicalSort;
|
||||
SmallPtrSet<const Instruction *, 32> Visited;
|
||||
for (auto &I : BB) {
|
||||
// First process side effecting and terminating instructions.
|
||||
if (!(isOutput(&I) || I.isTerminator()))
|
||||
continue;
|
||||
LLVM_DEBUG(dbgs() << "\tReordering from source effecting instruction: ";
|
||||
I.dump());
|
||||
reorderDefinition(&I, TopologicalSort, Visited);
|
||||
}
|
||||
|
||||
for (auto &I : BB) {
|
||||
// Process the remaining instructions.
|
||||
//
|
||||
// TODO: Do more a intelligent sorting of these instructions. For example,
|
||||
// seperate between dead instructinos and instructions used in another
|
||||
// block. Use properties of the CFG the order instructions that are used
|
||||
// in another block.
|
||||
if (Visited.contains(&I))
|
||||
continue;
|
||||
LLVM_DEBUG(dbgs() << "\tReordering from source instruction: "; I.dump());
|
||||
reorderDefinition(&I, TopologicalSort, Visited);
|
||||
}
|
||||
|
||||
LLVM_DEBUG(dbgs() << "Inserting instructions into: " << BB.getName()
|
||||
<< "\n");
|
||||
// Reorder based on the topological sort.
|
||||
while (!TopologicalSort.empty()) {
|
||||
auto *Instruction = TopologicalSort.top();
|
||||
auto FirstNonPHIOrDbgOrAlloca = BB.getFirstNonPHIOrDbgOrAlloca();
|
||||
if (auto *Call = dyn_cast<CallInst>(&*FirstNonPHIOrDbgOrAlloca)) {
|
||||
if (Call->getIntrinsicID() ==
|
||||
Intrinsic::experimental_convergence_entry ||
|
||||
Call->getIntrinsicID() == Intrinsic::experimental_convergence_loop)
|
||||
FirstNonPHIOrDbgOrAlloca++;
|
||||
}
|
||||
Instruction->moveBefore(&*FirstNonPHIOrDbgOrAlloca);
|
||||
TopologicalSort.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRNormalizer::reorderDefinition(
|
||||
Instruction *Definition, std::stack<Instruction *> &TopologicalSort,
|
||||
SmallPtrSet<const Instruction *, 32> &Visited) const {
|
||||
if (Visited.contains(Definition))
|
||||
return;
|
||||
Visited.insert(Definition);
|
||||
|
||||
{
|
||||
const auto *BasicBlock = Definition->getParent();
|
||||
const auto FirstNonPHIOrDbgOrAlloca =
|
||||
BasicBlock->getFirstNonPHIOrDbgOrAlloca();
|
||||
if (FirstNonPHIOrDbgOrAlloca == BasicBlock->end())
|
||||
return; // TODO: Is this necessary?
|
||||
if (Definition->comesBefore(&*FirstNonPHIOrDbgOrAlloca))
|
||||
return; // TODO: Do some kind of ordering for these instructions.
|
||||
}
|
||||
|
||||
for (auto &Operand : Definition->operands()) {
|
||||
if (auto *Op = dyn_cast<Instruction>(Operand)) {
|
||||
if (Op->getParent() != Definition->getParent())
|
||||
continue; // Only reorder instruction within the same basic block
|
||||
reorderDefinition(Op, TopologicalSort, Visited);
|
||||
}
|
||||
}
|
||||
|
||||
LLVM_DEBUG(dbgs() << "\t\tNext in topological sort: "; Definition->dump());
|
||||
if (Definition->isTerminator())
|
||||
return;
|
||||
if (auto *Call = dyn_cast<CallInst>(Definition)) {
|
||||
if (Call->isMustTailCall())
|
||||
return;
|
||||
if (Call->getIntrinsicID() == Intrinsic::experimental_deoptimize)
|
||||
return;
|
||||
if (Call->getIntrinsicID() == Intrinsic::experimental_convergence_entry)
|
||||
return;
|
||||
if (Call->getIntrinsicID() == Intrinsic::experimental_convergence_loop)
|
||||
return;
|
||||
}
|
||||
if (auto *BitCast = dyn_cast<BitCastInst>(Definition)) {
|
||||
if (auto *Call = dyn_cast<CallInst>(BitCast->getOperand(0))) {
|
||||
if (Call->isMustTailCall())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TopologicalSort.emplace(Definition);
|
||||
}
|
||||
|
||||
/// Reorders instruction's operands alphabetically. This method assumes
|
||||
/// that passed instruction is commutative. Changing the operand order
|
||||
/// in other instructions may change the semantics.
|
||||
///
|
||||
/// \param I Instruction whose operands will be reordered.
|
||||
void IRNormalizer::reorderInstructionOperandsByNames(Instruction *I) const {
|
||||
// This method assumes that passed I is commutative,
|
||||
// changing the order of operands in other instructions
|
||||
// may change the semantics.
|
||||
|
||||
// Instruction operands for further sorting.
|
||||
SmallVector<std::pair<std::string, Value *>, 4> Operands;
|
||||
|
||||
// Collect operands.
|
||||
for (auto &Op : I->operands()) {
|
||||
if (auto *V = dyn_cast<Value>(Op)) {
|
||||
if (isa<Instruction>(V)) {
|
||||
// This is an an instruction.
|
||||
Operands.push_back(std::pair<std::string, Value *>(V->getName(), V));
|
||||
} else {
|
||||
std::string TextRepresentation;
|
||||
raw_string_ostream Stream(TextRepresentation);
|
||||
Op->printAsOperand(Stream, false);
|
||||
Operands.push_back(std::pair<std::string, Value *>(Stream.str(), V));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort operands.
|
||||
sortCommutativeOperands(I, Operands);
|
||||
|
||||
// Reorder operands.
|
||||
unsigned Position = 0;
|
||||
for (auto &Op : I->operands()) {
|
||||
Op.set(Operands[Position].second);
|
||||
Position += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Reorders PHI node's values according to the names of corresponding basic
|
||||
/// blocks.
|
||||
///
|
||||
/// \param Phi PHI node to normalize.
|
||||
void IRNormalizer::reorderPHIIncomingValues(PHINode *Phi) const {
|
||||
// Values for further sorting.
|
||||
SmallVector<std::pair<Value *, BasicBlock *>, 2> Values;
|
||||
|
||||
// Collect blocks and corresponding values.
|
||||
for (auto &BB : Phi->blocks()) {
|
||||
Value *V = Phi->getIncomingValueForBlock(BB);
|
||||
Values.push_back(std::pair<Value *, BasicBlock *>(V, BB));
|
||||
}
|
||||
|
||||
// Sort values according to the name of a basic block.
|
||||
llvm::sort(Values, [](const std::pair<Value *, BasicBlock *> &LHS,
|
||||
const std::pair<Value *, BasicBlock *> &RHS) {
|
||||
return LHS.second->getName() < RHS.second->getName();
|
||||
});
|
||||
|
||||
// Swap.
|
||||
for (unsigned i = 0; i < Values.size(); ++i) {
|
||||
Phi->setIncomingBlock(i, Values[i].second);
|
||||
Phi->setIncomingValue(i, Values[i].first);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a vector of output instructions. An output is an instruction which
|
||||
/// has side-effects or is ReturnInst. Uses isOutput().
|
||||
///
|
||||
/// \see isOutput()
|
||||
/// \param F Function to collect outputs from.
|
||||
SmallVector<Instruction *, 16>
|
||||
IRNormalizer::collectOutputInstructions(Function &F) const {
|
||||
// Output instructions are collected top-down in each function,
|
||||
// any change may break the def-use chain in reordering methods.
|
||||
SmallVector<Instruction *, 16> Outputs;
|
||||
for (auto &I : instructions(F))
|
||||
if (isOutput(&I))
|
||||
Outputs.push_back(&I);
|
||||
return Outputs;
|
||||
}
|
||||
|
||||
/// Helper method checking whether the instruction may have side effects or is
|
||||
/// ReturnInst.
|
||||
///
|
||||
/// \param I Considered instruction.
|
||||
bool IRNormalizer::isOutput(const Instruction *I) const {
|
||||
// Outputs are such instructions which may have side effects or is ReturnInst.
|
||||
return I->mayHaveSideEffects() || isa<ReturnInst>(I);
|
||||
}
|
||||
|
||||
/// Helper method checking whether the instruction has users and only
|
||||
/// immediate operands.
|
||||
///
|
||||
/// \param I Considered instruction.
|
||||
bool IRNormalizer::isInitialInstruction(const Instruction *I) const {
|
||||
// Initial instructions are such instructions whose values are used by
|
||||
// other instructions, yet they only depend on immediate values.
|
||||
return !I->user_empty() && hasOnlyImmediateOperands(I);
|
||||
}
|
||||
|
||||
/// Helper method checking whether the instruction has only immediate operands.
|
||||
///
|
||||
/// \param I Considered instruction.
|
||||
bool IRNormalizer::hasOnlyImmediateOperands(const Instruction *I) const {
|
||||
for (const auto &Op : I->operands())
|
||||
if (isa<Instruction>(Op))
|
||||
return false; // Found non-immediate operand (instruction).
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Helper method returning indices (distance from the beginning of the basic
|
||||
/// block) of outputs using the \p I (eliminates repetitions). Walks down the
|
||||
/// def-use tree recursively.
|
||||
///
|
||||
/// \param I Considered instruction.
|
||||
/// \param Visited Set of visited instructions.
|
||||
SetVector<int> IRNormalizer::getOutputFootprint(
|
||||
Instruction *I, SmallPtrSet<const Instruction *, 32> &Visited) const {
|
||||
|
||||
// Vector containing indexes of outputs (no repetitions),
|
||||
// which use I in the order of walking down the def-use tree.
|
||||
SetVector<int> Outputs;
|
||||
|
||||
if (!Visited.count(I)) {
|
||||
Visited.insert(I);
|
||||
|
||||
if (isOutput(I)) {
|
||||
// Gets output instruction's parent function.
|
||||
Function *Func = I->getParent()->getParent();
|
||||
|
||||
// Finds and inserts the index of the output to the vector.
|
||||
unsigned Count = 0;
|
||||
for (const auto &B : *Func) {
|
||||
for (const auto &E : B) {
|
||||
if (&E == I)
|
||||
Outputs.insert(Count);
|
||||
Count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns to the used instruction.
|
||||
return Outputs;
|
||||
}
|
||||
|
||||
for (auto *U : I->users()) {
|
||||
if (auto *UI = dyn_cast<Instruction>(U)) {
|
||||
// Vector for outputs which use UI.
|
||||
SetVector<int> OutputsUsingUI = getOutputFootprint(UI, Visited);
|
||||
// Insert the indexes of outputs using UI.
|
||||
Outputs.insert(OutputsUsingUI.begin(), OutputsUsingUI.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return to the used instruction.
|
||||
return Outputs;
|
||||
}
|
||||
|
||||
PreservedAnalyses IRNormalizerPass::run(Function &F,
|
||||
FunctionAnalysisManager &AM) const {
|
||||
IRNormalizer{}.runOnFunction(F);
|
||||
return PreservedAnalyses::all();
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
||||
; RUN: opt -S -passes=normalize < %s | FileCheck %s
|
||||
|
||||
define i32 @foo(i32) {
|
||||
; CHECK-LABEL: define i32 @foo(
|
||||
; CHECK-SAME: i32 [[A0:%.*]]) {
|
||||
; CHECK-NEXT: [[BB17254:.*:]]
|
||||
; CHECK-NEXT: %"vl24903([[A0]], 2)" = add i32 [[A0]], 2
|
||||
; CHECK-NEXT: %"op10412(vl24903)" = add i32 6, %"vl24903([[A0]], 2)"
|
||||
; CHECK-NEXT: ret i32 %"op10412(vl24903)"
|
||||
;
|
||||
entry:
|
||||
%a = add i32 %0, 2
|
||||
|
||||
%b = add i32 %a, 6
|
||||
|
||||
ret i32 %b
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
||||
; RUN: opt -S -passes=normalize < %s | FileCheck %s
|
||||
|
||||
define i32 @foo(i32, i32) {
|
||||
; CHECK-LABEL: define i32 @foo(
|
||||
; CHECK-SAME: i32 [[A0:%.*]], i32 [[A1:%.*]]) {
|
||||
; CHECK-NEXT: [[BB17254:.*:]]
|
||||
; CHECK-NEXT: %"vl20416([[A0]], [[A1]])" = mul i32 [[A0]], [[A1]]
|
||||
; CHECK-NEXT: ret i32 %"vl20416([[A0]], [[A1]])"
|
||||
;
|
||||
%tmp = mul i32 %0, %1
|
||||
ret i32 %tmp
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
||||
; RUN: opt -S -passes=normalize < %s | FileCheck %s
|
||||
|
||||
define i32 @foo(i32 %a0) {
|
||||
; CHECK-LABEL: define i32 @foo(
|
||||
; CHECK-SAME: i32 [[A0:%.*]]) {
|
||||
; CHECK-NEXT: [[BB17254:.*:]]
|
||||
; CHECK-NEXT: %"vl12603([[A0]], 2)" = add i32 [[A0]], 2
|
||||
; CHECK-NEXT: ret i32 %"vl12603([[A0]], 2)"
|
||||
;
|
||||
entry:
|
||||
%a = add i32 %a0, 2
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
define i32 @bar(i32 %a0) {
|
||||
; CHECK-LABEL: define i32 @bar(
|
||||
; CHECK-SAME: i32 [[A0:%.*]]) {
|
||||
; CHECK-NEXT: [[BB17254:.*:]]
|
||||
; CHECK-NEXT: %"vl76167([[A0]], 2)" = add i32 [[A0]], 2
|
||||
; CHECK-NEXT: %"op10412(vl76167)" = add i32 6, %"vl76167([[A0]], 2)"
|
||||
; CHECK-NEXT: %"op10412(op10412)" = add i32 8, %"op10412(vl76167)"
|
||||
; CHECK-NEXT: ret i32 %"op10412(op10412)"
|
||||
;
|
||||
entry:
|
||||
%a = add i32 %a0, 2
|
||||
%b = add i32 %a, 6
|
||||
%c = add i32 %b, 8
|
||||
ret i32 %c
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
||||
; RUN: opt -S -passes=normalize < %s | FileCheck %s
|
||||
|
||||
; Function Attrs: convergent nounwind readnone
|
||||
define i32 @nested(i32 %src) #0 {
|
||||
; CHECK-LABEL: define i32 @nested(
|
||||
; CHECK-SAME: i32 [[A0:%.*]]) #[[ATTR0:[0-9]+]] {
|
||||
; CHECK-NEXT: [[BB15160:.*:]]
|
||||
; CHECK-NEXT: [[T1:%.*]] = call token @llvm.experimental.convergence.entry()
|
||||
; CHECK-NEXT: %"vl15001llvm.experimental.convergence.anchor()" = call token @llvm.experimental.convergence.anchor()
|
||||
; CHECK-NEXT: %"op68297llvm.amdgcn.readfirstlane.i32([[A0]], vl15001llvm.experimental.convergence.anchor())" = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[A0]]) [ "convergencectrl"(token %"vl15001llvm.experimental.convergence.anchor()") ]
|
||||
; CHECK-NEXT: ret i32 undef
|
||||
;
|
||||
%t1 = call token @llvm.experimental.convergence.entry()
|
||||
%t2 = call token @llvm.experimental.convergence.anchor()
|
||||
%r2 = call i32 @llvm.amdgcn.readfirstlane(i32 %src) [ "convergencectrl"(token %t2) ]
|
||||
ret i32 undef
|
||||
}
|
||||
|
||||
; Function Attrs: convergent nounwind readnone
|
||||
declare i32 @llvm.amdgcn.readfirstlane(i32) #0
|
||||
|
||||
declare token @llvm.experimental.convergence.entry()
|
||||
|
||||
declare token @llvm.experimental.convergence.anchor()
|
||||
|
||||
attributes #0 = { convergent nounwind readnone }
|
||||
@@ -1,21 +0,0 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
||||
; RUN: opt -S -passes=normalize < %s | FileCheck %s
|
||||
|
||||
define fastcc void @foo.resume_musttail(ptr %FramePtr) {
|
||||
; CHECK-LABEL: define fastcc void @foo.resume_musttail(
|
||||
; CHECK-SAME: ptr [[A0:%.*]]) {
|
||||
; CHECK-NEXT: [[BB15160:.*:]]
|
||||
; CHECK-NEXT: [[TMP0:%.*]] = tail call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
|
||||
; CHECK-NEXT: musttail call fastcc void undef(ptr null)
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
entry:
|
||||
%0 = tail call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
|
||||
musttail call fastcc void undef(ptr null)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
|
||||
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr) #0
|
||||
|
||||
attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) }
|
||||
@@ -1,18 +0,0 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
||||
; RUN: opt -S -passes=normalize < %s | FileCheck %s
|
||||
|
||||
target datalayout = "e-p:32:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-i64:32:32-f32:32:32-f64:32:32-v64:32:64-v128:32:128-a0:0:32-n32"
|
||||
|
||||
define void @test1() {
|
||||
; CHECK-LABEL: define void @test1() {
|
||||
; CHECK-NEXT: [[BB15160:.*:]]
|
||||
; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr null, align 1
|
||||
; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ]
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
%1 = load i8, ptr null, align 1
|
||||
call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ]
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @llvm.experimental.deoptimize.isVoid(...)
|
||||
@@ -1,20 +0,0 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
||||
; RUN: opt -S -passes=normalize < %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-ni:1-p2:32:8:8:32-ni:2"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
declare void @llvm.experimental.deoptimize.isVoid(...)
|
||||
|
||||
define void @widget() {
|
||||
; CHECK-LABEL: define void @widget() {
|
||||
; CHECK-NEXT: [[BB15160:.*:]]
|
||||
; CHECK-NEXT: [[TMP3:%.*]] = trunc i64 0 to i32
|
||||
; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid(i32 0) [ "deopt"() ]
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
bb:
|
||||
%tmp3 = trunc i64 0 to i32
|
||||
call void (...) @llvm.experimental.deoptimize.isVoid(i32 0) [ "deopt"() ]
|
||||
ret void
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
||||
; RUN: opt -S -passes=normalize < %s | FileCheck %s
|
||||
|
||||
define void @test(ptr, i32) {
|
||||
; CHECK-LABEL: define void @test(
|
||||
; CHECK-SAME: ptr [[A0:%.*]], i32 [[A1:%.*]]) {
|
||||
; CHECK-NEXT: [[BB76951:.*]]:
|
||||
; CHECK-NEXT: %"vl72693([[A1]], 1)" = add i32 [[A1]], 1
|
||||
; CHECK-NEXT: br label %[[BB16110:.*]]
|
||||
; CHECK: [[BB16110]]:
|
||||
; CHECK-NEXT: %"op10912(op18080, vl72693)" = phi i32 [ %"op18080(op10412, op17645)", %[[BB16110]] ], [ %"vl72693([[A1]], 1)", %[[BB76951]] ]
|
||||
; CHECK-NEXT: %"op10912(op17645, vl72693)" = phi i32 [ %"op17645(op10912)70", %[[BB16110]] ], [ %"vl72693([[A1]], 1)", %[[BB76951]] ]
|
||||
; CHECK-NEXT: %"op15084(op10912)" = mul i32 %"op10912(op18080, vl72693)", undef
|
||||
; CHECK-NEXT: %"op16562(op15084)" = xor i32 -1, %"op15084(op10912)"
|
||||
; CHECK-NEXT: %"op44627(op10912, op16562)" = add i32 %"op10912(op18080, vl72693)", %"op16562(op15084)"
|
||||
; CHECK-NEXT: %"op17645(op10912)" = add i32 -1, %"op10912(op17645, vl72693)"
|
||||
; CHECK-NEXT: %"op18080(op17645, op44627)" = add i32 %"op17645(op10912)", %"op44627(op10912, op16562)"
|
||||
; CHECK-NEXT: %"op17720(op15084, op18080)" = mul i32 %"op15084(op10912)", %"op18080(op17645, op44627)"
|
||||
; CHECK-NEXT: %"op16562(op17720)" = xor i32 -1, %"op17720(op15084, op18080)"
|
||||
; CHECK-NEXT: %"op17430(op16562, op18080)" = add i32 %"op16562(op17720)", %"op18080(op17645, op44627)"
|
||||
; CHECK-NEXT: %"op10412(op17430)" = add i32 %"op17430(op16562, op18080)", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)" = mul i32 %"op10412(op17430)", %"op17720(op15084, op18080)"
|
||||
; CHECK-NEXT: %"op16562(op17720)1" = xor i32 -1, %"op17720(op10412, op17720)"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)" = add i32 %"op10412(op17430)", %"op16562(op17720)1"
|
||||
; CHECK-NEXT: %"op10412(op17430)2" = add i32 %"op17430(op10412, op16562)", undef
|
||||
; CHECK-NEXT: %"op10412(op10412)" = add i32 %"op10412(op17430)2", undef
|
||||
; CHECK-NEXT: %"op10412(op10412)3" = add i32 %"op10412(op10412)", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)4" = mul i32 %"op10412(op17430)2", %"op17720(op10412, op17720)"
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)5" = mul i32 %"op10412(op10412)3", %"op17720(op10412, op17720)4"
|
||||
; CHECK-NEXT: %"op16562(op17720)6" = xor i32 -1, %"op17720(op10412, op17720)5"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)7" = add i32 %"op10412(op10412)3", %"op16562(op17720)6"
|
||||
; CHECK-NEXT: %"op10412(op17430)8" = add i32 %"op17430(op10412, op16562)7", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)9" = mul i32 %"op10412(op17430)8", %"op17720(op10412, op17720)5"
|
||||
; CHECK-NEXT: %"op16562(op17720)10" = xor i32 -1, %"op17720(op10412, op17720)9"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)11" = add i32 %"op10412(op17430)8", %"op16562(op17720)10"
|
||||
; CHECK-NEXT: %"op10412(op17430)12" = add i32 %"op17430(op10412, op16562)11", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)13" = mul i32 %"op10412(op17430)12", %"op17720(op10412, op17720)9"
|
||||
; CHECK-NEXT: %"op16562(op17720)14" = xor i32 -1, %"op17720(op10412, op17720)13"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)15" = add i32 %"op10412(op17430)12", %"op16562(op17720)14"
|
||||
; CHECK-NEXT: %"op10412(op17430)16" = add i32 %"op17430(op10412, op16562)15", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)17" = mul i32 %"op10412(op17430)16", %"op17720(op10412, op17720)13"
|
||||
; CHECK-NEXT: %"op16562(op17720)18" = xor i32 -1, %"op17720(op10412, op17720)17"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)19" = add i32 %"op10412(op17430)16", %"op16562(op17720)18"
|
||||
; CHECK-NEXT: %"op10412(op17430)20" = add i32 %"op17430(op10412, op16562)19", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)21" = mul i32 %"op10412(op17430)20", %"op17720(op10412, op17720)17"
|
||||
; CHECK-NEXT: %"op16562(op17720)22" = xor i32 -1, %"op17720(op10412, op17720)21"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)23" = add i32 %"op10412(op17430)20", %"op16562(op17720)22"
|
||||
; CHECK-NEXT: %"op17645(op10912)24" = add i32 -9, %"op10912(op17645, vl72693)"
|
||||
; CHECK-NEXT: %"op18080(op17430, op17645)" = add i32 %"op17430(op10412, op16562)23", %"op17645(op10912)24"
|
||||
; CHECK-NEXT: %"op17720(op17720, op18080)" = mul i32 %"op17720(op10412, op17720)21", %"op18080(op17430, op17645)"
|
||||
; CHECK-NEXT: %"op16562(op17720)25" = xor i32 -1, %"op17720(op17720, op18080)"
|
||||
; CHECK-NEXT: %"op17430(op16562, op18080)26" = add i32 %"op16562(op17720)25", %"op18080(op17430, op17645)"
|
||||
; CHECK-NEXT: %"op10412(op17430)27" = add i32 %"op17430(op16562, op18080)26", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)28" = mul i32 %"op10412(op17430)27", %"op17720(op17720, op18080)"
|
||||
; CHECK-NEXT: %"op16562(op17720)29" = xor i32 -1, %"op17720(op10412, op17720)28"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)30" = add i32 %"op10412(op17430)27", %"op16562(op17720)29"
|
||||
; CHECK-NEXT: %"op10412(op17430)31" = add i32 %"op17430(op10412, op16562)30", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)32" = mul i32 %"op10412(op17430)31", %"op17720(op10412, op17720)28"
|
||||
; CHECK-NEXT: %"op16562(op17720)33" = xor i32 -1, %"op17720(op10412, op17720)32"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)34" = add i32 %"op10412(op17430)31", %"op16562(op17720)33"
|
||||
; CHECK-NEXT: %"op10412(op17430)35" = add i32 %"op17430(op10412, op16562)34", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)36" = mul i32 %"op10412(op17430)35", %"op17720(op10412, op17720)32"
|
||||
; CHECK-NEXT: %"op16562(op17720)37" = xor i32 -1, %"op17720(op10412, op17720)36"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)38" = add i32 %"op10412(op17430)35", %"op16562(op17720)37"
|
||||
; CHECK-NEXT: %"op10412(op17430)39" = add i32 %"op17430(op10412, op16562)38", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)40" = mul i32 %"op10412(op17430)39", %"op17720(op10412, op17720)36"
|
||||
; CHECK-NEXT: %"op16562(op17720)41" = xor i32 -1, %"op17720(op10412, op17720)40"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)42" = add i32 %"op10412(op17430)39", %"op16562(op17720)41"
|
||||
; CHECK-NEXT: %"op17645(op10912)43" = add i32 -14, %"op10912(op17645, vl72693)"
|
||||
; CHECK-NEXT: %"op18080(op17430, op17645)44" = add i32 %"op17430(op10412, op16562)42", %"op17645(op10912)43"
|
||||
; CHECK-NEXT: %"op17720(op17720, op18080)45" = mul i32 %"op17720(op10412, op17720)40", %"op18080(op17430, op17645)44"
|
||||
; CHECK-NEXT: %"op16562(op17720)46" = xor i32 -1, %"op17720(op17720, op18080)45"
|
||||
; CHECK-NEXT: %"op17430(op16562, op18080)47" = add i32 %"op16562(op17720)46", %"op18080(op17430, op17645)44"
|
||||
; CHECK-NEXT: %"op10412(op17430)48" = add i32 %"op17430(op16562, op18080)47", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)49" = mul i32 %"op10412(op17430)48", %"op17720(op17720, op18080)45"
|
||||
; CHECK-NEXT: %"op16562(op17720)50" = xor i32 -1, %"op17720(op10412, op17720)49"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)51" = add i32 %"op10412(op17430)48", %"op16562(op17720)50"
|
||||
; CHECK-NEXT: %"op10412(op17430)52" = add i32 %"op17430(op10412, op16562)51", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)53" = mul i32 %"op10412(op17430)52", %"op17720(op10412, op17720)49"
|
||||
; CHECK-NEXT: %"op16562(op17720)54" = xor i32 -1, %"op17720(op10412, op17720)53"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)55" = add i32 %"op10412(op17430)52", %"op16562(op17720)54"
|
||||
; CHECK-NEXT: %"op10412(op17430)56" = add i32 %"op17430(op10412, op16562)55", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)57" = mul i32 %"op10412(op17430)56", %"op17720(op10412, op17720)53"
|
||||
; CHECK-NEXT: %"op16562(op17720)58" = xor i32 -1, %"op17720(op10412, op17720)57"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)59" = add i32 %"op10412(op17430)56", %"op16562(op17720)58"
|
||||
; CHECK-NEXT: %"op10412(op17430)60" = add i32 %"op17430(op10412, op16562)59", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)61" = mul i32 %"op10412(op17430)60", %"op17720(op10412, op17720)57"
|
||||
; CHECK-NEXT: %"op16562(op17720)62" = xor i32 -1, %"op17720(op10412, op17720)61"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)63" = add i32 %"op10412(op17430)60", %"op16562(op17720)62"
|
||||
; CHECK-NEXT: %"op10412(op17430)64" = add i32 %"op17430(op10412, op16562)63", undef
|
||||
; CHECK-NEXT: %"op17720(op10412, op17720)65" = mul i32 %"op10412(op17430)64", %"op17720(op10412, op17720)61"
|
||||
; CHECK-NEXT: %"op16562(op17720)66" = xor i32 -1, %"op17720(op10412, op17720)65"
|
||||
; CHECK-NEXT: %"op17430(op10412, op16562)67" = add i32 %"op10412(op17430)64", %"op16562(op17720)66"
|
||||
; CHECK-NEXT: %"op10412(op17430)68" = add i32 %"op17430(op10412, op16562)67", undef
|
||||
; CHECK-NEXT: %"op10412(op10412)69" = add i32 %"op10412(op17430)68", undef
|
||||
; CHECK-NEXT: %"op17645(op10912)70" = add i32 -21, %"op10912(op17645, vl72693)"
|
||||
; CHECK-NEXT: %"op18080(op10412, op17645)" = add i32 %"op10412(op10412)69", %"op17645(op10912)70"
|
||||
; CHECK-NEXT: store i32 %"op18080(op10412, op17645)", ptr [[A0]], align 4
|
||||
; CHECK-NEXT: br label %[[BB16110]]
|
||||
;
|
||||
bb:
|
||||
%a = add i32 %1, 1
|
||||
br label %bb1
|
||||
|
||||
bb1: ; preds = %bb1, %bb
|
||||
%tmp = phi i32 [ %a, %bb ], [ %tmp87, %bb1 ]
|
||||
%tmp2 = phi i32 [ %a, %bb ], [ %tmp86, %bb1 ]
|
||||
%tmp3 = mul i32 %tmp, undef
|
||||
%tmp4 = xor i32 %tmp3, -1
|
||||
%tmp5 = add i32 %tmp, %tmp4
|
||||
%tmp6 = add i32 %tmp2, -1
|
||||
%tmp7 = add i32 %tmp5, %tmp6
|
||||
%tmp8 = mul i32 %tmp7, %tmp3
|
||||
%tmp9 = xor i32 %tmp8, -1
|
||||
%tmp10 = add i32 %tmp7, %tmp9
|
||||
%tmp11 = add i32 %tmp10, undef
|
||||
%tmp12 = mul i32 %tmp11, %tmp8
|
||||
%tmp13 = xor i32 %tmp12, -1
|
||||
%tmp14 = add i32 %tmp11, %tmp13
|
||||
%tmp15 = add i32 %tmp14, undef
|
||||
%tmp16 = mul i32 %tmp15, %tmp12
|
||||
%tmp17 = add i32 %tmp15, undef
|
||||
%tmp18 = add i32 %tmp17, undef
|
||||
%tmp19 = mul i32 %tmp18, %tmp16
|
||||
%tmp20 = xor i32 %tmp19, -1
|
||||
%tmp21 = add i32 %tmp18, %tmp20
|
||||
%tmp22 = add i32 %tmp21, undef
|
||||
%tmp23 = mul i32 %tmp22, %tmp19
|
||||
%tmp24 = xor i32 %tmp23, -1
|
||||
%tmp25 = add i32 %tmp22, %tmp24
|
||||
%tmp26 = add i32 %tmp25, undef
|
||||
%tmp27 = mul i32 %tmp26, %tmp23
|
||||
%tmp28 = xor i32 %tmp27, -1
|
||||
%tmp29 = add i32 %tmp26, %tmp28
|
||||
%tmp30 = add i32 %tmp29, undef
|
||||
%tmp31 = mul i32 %tmp30, %tmp27
|
||||
%tmp32 = xor i32 %tmp31, -1
|
||||
%tmp33 = add i32 %tmp30, %tmp32
|
||||
%tmp34 = add i32 %tmp33, undef
|
||||
%tmp35 = mul i32 %tmp34, %tmp31
|
||||
%tmp36 = xor i32 %tmp35, -1
|
||||
%tmp37 = add i32 %tmp34, %tmp36
|
||||
%tmp38 = add i32 %tmp2, -9
|
||||
%tmp39 = add i32 %tmp37, %tmp38
|
||||
%tmp40 = mul i32 %tmp39, %tmp35
|
||||
%tmp41 = xor i32 %tmp40, -1
|
||||
%tmp42 = add i32 %tmp39, %tmp41
|
||||
%tmp43 = add i32 %tmp42, undef
|
||||
%tmp44 = mul i32 %tmp43, %tmp40
|
||||
%tmp45 = xor i32 %tmp44, -1
|
||||
%tmp46 = add i32 %tmp43, %tmp45
|
||||
%tmp47 = add i32 %tmp46, undef
|
||||
%tmp48 = mul i32 %tmp47, %tmp44
|
||||
%tmp49 = xor i32 %tmp48, -1
|
||||
%tmp50 = add i32 %tmp47, %tmp49
|
||||
%tmp51 = add i32 %tmp50, undef
|
||||
%tmp52 = mul i32 %tmp51, %tmp48
|
||||
%tmp53 = xor i32 %tmp52, -1
|
||||
%tmp54 = add i32 %tmp51, %tmp53
|
||||
%tmp55 = add i32 %tmp54, undef
|
||||
%tmp56 = mul i32 %tmp55, %tmp52
|
||||
%tmp57 = xor i32 %tmp56, -1
|
||||
%tmp58 = add i32 %tmp55, %tmp57
|
||||
%tmp59 = add i32 %tmp2, -14
|
||||
%tmp60 = add i32 %tmp58, %tmp59
|
||||
%tmp61 = mul i32 %tmp60, %tmp56
|
||||
%tmp62 = xor i32 %tmp61, -1
|
||||
%tmp63 = add i32 %tmp60, %tmp62
|
||||
%tmp64 = add i32 %tmp63, undef
|
||||
%tmp65 = mul i32 %tmp64, %tmp61
|
||||
%tmp66 = xor i32 %tmp65, -1
|
||||
%tmp67 = add i32 %tmp64, %tmp66
|
||||
%tmp68 = add i32 %tmp67, undef
|
||||
%tmp69 = mul i32 %tmp68, %tmp65
|
||||
%tmp70 = xor i32 %tmp69, -1
|
||||
%tmp71 = add i32 %tmp68, %tmp70
|
||||
%tmp72 = add i32 %tmp71, undef
|
||||
%tmp73 = mul i32 %tmp72, %tmp69
|
||||
%tmp74 = xor i32 %tmp73, -1
|
||||
%tmp75 = add i32 %tmp72, %tmp74
|
||||
%tmp76 = add i32 %tmp75, undef
|
||||
%tmp77 = mul i32 %tmp76, %tmp73
|
||||
%tmp78 = xor i32 %tmp77, -1
|
||||
%tmp79 = add i32 %tmp76, %tmp78
|
||||
%tmp80 = add i32 %tmp79, undef
|
||||
%tmp81 = mul i32 %tmp80, %tmp77
|
||||
%tmp82 = xor i32 %tmp81, -1
|
||||
%tmp83 = add i32 %tmp80, %tmp82
|
||||
%tmp84 = add i32 %tmp83, undef
|
||||
%tmp85 = add i32 %tmp84, undef
|
||||
%tmp86 = add i32 %tmp2, -21
|
||||
%tmp87 = add i32 %tmp85, %tmp86
|
||||
store i32 %tmp87, ptr %0
|
||||
br label %bb1
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
||||
; RUN: opt -S -passes=normalize < %s | FileCheck %s
|
||||
|
||||
define double @foo(double %a0, double %a1) {
|
||||
; CHECK-LABEL: define double @foo(
|
||||
; CHECK-SAME: double [[A0:%.*]], double [[A1:%.*]]) {
|
||||
; CHECK-NEXT: [[BB17254:.*:]]
|
||||
; CHECK-NEXT: %"vl93562([[A0]], 2.000000e+00)" = fmul double [[A0]], 2.000000e+00
|
||||
; CHECK-NEXT: %"op95858(vl93562)" = fmul double 6.000000e+00, %"vl93562([[A0]], 2.000000e+00)"
|
||||
; CHECK-NEXT: [[A:%.*]] = fmul double [[A0]], [[A1]]
|
||||
; CHECK-NEXT: [[C:%.*]] = fmul double 6.000000e+00, [[A]]
|
||||
; CHECK-NEXT: ret double %"op95858(vl93562)"
|
||||
;
|
||||
entry:
|
||||
%a = fmul double %a0, %a1
|
||||
%b = fmul double %a0, 2.000000e+00
|
||||
%c = fmul double %a, 6.000000e+00
|
||||
%d = fmul double %b, 6.000000e+00
|
||||
ret double %d
|
||||
}
|
||||
|
||||
declare double @bir()
|
||||
|
||||
declare double @bar()
|
||||
|
||||
define double @baz(double %x) {
|
||||
; CHECK-LABEL: define double @baz(
|
||||
; CHECK-SAME: double [[A0:%.*]]) {
|
||||
; CHECK-NEXT: [[BB76951:.*:]]
|
||||
; CHECK-NEXT: [[IFCOND:%.*]] = fcmp one double [[A0]], 0.000000e+00
|
||||
; CHECK-NEXT: br i1 [[IFCOND]], label %[[BB91455:.*]], label %[[BB914551:.*]]
|
||||
; CHECK: [[BB91455]]:
|
||||
; CHECK-NEXT: %"vl15001bir()" = call double @bir()
|
||||
; CHECK-NEXT: br label %[[BB17254:.*]]
|
||||
; CHECK: [[BB914551]]:
|
||||
; CHECK-NEXT: %"vl69719bar()" = call double @bar()
|
||||
; CHECK-NEXT: br label %[[BB17254]]
|
||||
; CHECK: [[BB17254]]:
|
||||
; CHECK-NEXT: %"op19734(vl15001, vl69719)" = phi double [ %"vl15001bir()", %[[BB91455]] ], [ %"vl69719bar()", %[[BB914551]] ]
|
||||
; CHECK-NEXT: ret double %"op19734(vl15001, vl69719)"
|
||||
;
|
||||
entry:
|
||||
%ifcond = fcmp one double %x, 0.000000e+00
|
||||
br i1 %ifcond, label %then, label %else
|
||||
|
||||
then: ; preds = %entry
|
||||
%calltmp = call double @bir()
|
||||
br label %ifcont
|
||||
|
||||
else: ; preds = %entry
|
||||
%calltmp1 = call double @bar()
|
||||
br label %ifcont
|
||||
|
||||
ifcont: ; preds = %else, %then
|
||||
%iftmp = phi double [ %calltmp, %then ], [ %calltmp1, %else ]
|
||||
ret double %iftmp
|
||||
}
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
|
||||
; RUN: opt -S -passes=normalize -verify-each -norm-rename-all=false < %s | FileCheck %s
|
||||
|
||||
define void @foo() {
|
||||
; CHECK-LABEL: define void @foo() {
|
||||
; CHECK-NEXT: bb17254:
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @empty_basic_block() {
|
||||
; CHECK-LABEL: define void @empty_basic_block() {
|
||||
; CHECK-NEXT: exit:
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
exit:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @effecting()
|
||||
|
||||
; Place dead instruction(s) before the terminator
|
||||
define void @call_effecting() {
|
||||
; CHECK-LABEL: define void @call_effecting() {
|
||||
; CHECK-NEXT: bb15160:
|
||||
; CHECK-NEXT: call void @effecting()
|
||||
; CHECK-NEXT: [[TMP0:%.*]] = add i32 0, 1
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
%1 = add i32 0, 1
|
||||
call void @effecting()
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @dont_move_above_phi() {
|
||||
; CHECK-LABEL: define void @dont_move_above_phi() {
|
||||
; CHECK-NEXT: bb76951:
|
||||
; CHECK-NEXT: br label [[EXIT:%.*]]
|
||||
; CHECK: exit:
|
||||
; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ 0, [[BB76951:%.*]] ]
|
||||
; CHECK-NEXT: call void @effecting()
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
br label %exit
|
||||
exit:
|
||||
%1 = phi i32 [0, %0]
|
||||
call void @effecting()
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @dont_move_above_alloca() {
|
||||
; CHECK-LABEL: define void @dont_move_above_alloca() {
|
||||
; CHECK-NEXT: bb15160:
|
||||
; CHECK-NEXT: [[TMP0:%.*]] = alloca i32, align 4
|
||||
; CHECK-NEXT: call void @effecting()
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
%1 = alloca i32
|
||||
call void @effecting()
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @effecting1()
|
||||
|
||||
define void @dont_reorder_effecting() {
|
||||
; CHECK-LABEL: define void @dont_reorder_effecting() {
|
||||
; CHECK-NEXT: bb10075:
|
||||
; CHECK-NEXT: call void @effecting()
|
||||
; CHECK-NEXT: call void @effecting1()
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
call void @effecting()
|
||||
call void @effecting1()
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @effecting2(i32)
|
||||
|
||||
define void @dont_reorder_effecting1() {
|
||||
; CHECK-LABEL: define void @dont_reorder_effecting1() {
|
||||
; CHECK-NEXT: bb10075:
|
||||
; CHECK-NEXT: [[ONE:%.*]] = add i32 1, 1
|
||||
; CHECK-NEXT: call void @effecting2(i32 [[ONE]])
|
||||
; CHECK-NEXT: [[TWO:%.*]] = add i32 2, 2
|
||||
; CHECK-NEXT: call void @effecting2(i32 [[TWO]])
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
%one = add i32 1, 1
|
||||
%two = add i32 2, 2
|
||||
call void @effecting2(i32 %one)
|
||||
call void @effecting2(i32 %two)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @dont_reorder_across_blocks() {
|
||||
; CHECK-LABEL: define void @dont_reorder_across_blocks() {
|
||||
; CHECK-NEXT: bb76951:
|
||||
; CHECK-NEXT: [[ONE:%.*]] = add i32 1, 1
|
||||
; CHECK-NEXT: br label [[EXIT:%.*]]
|
||||
; CHECK: exit:
|
||||
; CHECK-NEXT: call void @effecting2(i32 [[ONE]])
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
%one = add i32 1, 1
|
||||
br label %exit
|
||||
exit:
|
||||
call void @effecting2(i32 %one)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @independentldst(ptr %a, ptr %b) {
|
||||
; CHECK-LABEL: define void @independentldst(
|
||||
; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]]) {
|
||||
; CHECK-NEXT: bb10495:
|
||||
; CHECK-NEXT: %"vl12961([[B]])" = load i32, ptr [[B]], align 4
|
||||
; CHECK-NEXT: store i32 %"vl12961([[B]])", ptr [[A]], align 4
|
||||
; CHECK-NEXT: %"vl89528([[A]])" = load i32, ptr [[A]], align 4
|
||||
; CHECK-NEXT: store i32 %"vl89528([[A]])", ptr [[B]], align 4
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
%2 = load i32, ptr %a
|
||||
%3 = load i32, ptr %b
|
||||
store i32 %3, ptr %a
|
||||
store i32 %2, ptr %b
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @multiple_use_ld(ptr %a, ptr %b) {
|
||||
; CHECK-LABEL: define void @multiple_use_ld(
|
||||
; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]]) {
|
||||
; CHECK-NEXT: bb14927:
|
||||
; CHECK-NEXT: %"vl16793([[A]])" = load i32, ptr [[A]], align 4
|
||||
; CHECK-NEXT: store i32 %"vl16793([[A]])", ptr [[A]], align 4
|
||||
; CHECK-NEXT: %"vl89528([[B]])" = load i32, ptr [[B]], align 4
|
||||
; CHECK-NEXT: store i32 %"vl89528([[B]])", ptr [[B]], align 4
|
||||
; CHECK-NEXT: store i32 %"vl16793([[A]])", ptr [[A]], align 4
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
%2 = load i32, ptr %a
|
||||
store i32 %2, ptr %a
|
||||
%3 = load i32, ptr %b
|
||||
store i32 %3, ptr %b
|
||||
store i32 %2, ptr %a
|
||||
ret void
|
||||
}
|
||||
|
||||
; This is an incorrect transformation. Moving the store above the load could
|
||||
; change the loaded value. To do this, the pointers would need to be `noalias`.
|
||||
define void @undef_st(ptr %a, ptr %b) {
|
||||
; CHECK-LABEL: define void @undef_st(
|
||||
; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]]) {
|
||||
; CHECK-NEXT: bb10495:
|
||||
; CHECK-NEXT: store i32 undef, ptr [[B]], align 4
|
||||
; CHECK-NEXT: %"vl16028([[A]])" = load i32, ptr [[A]], align 4
|
||||
; CHECK-NEXT: store i32 %"vl16028([[A]])", ptr [[A]], align 4
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
%2 = load i32, ptr %a
|
||||
store i32 undef, ptr %b
|
||||
store i32 %2, ptr %a
|
||||
ret void
|
||||
}
|
||||
Reference in New Issue
Block a user