2019-01-04 04:16:33 +08:00
|
|
|
//=======- CaptureTrackingTest.cpp - Unit test for the Capture Tracking ---===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2019-01-04 04:16:33 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "llvm/Analysis/CaptureTracking.h"
|
|
|
|
#include "llvm/AsmParser/Parser.h"
|
|
|
|
#include "llvm/IR/Dominators.h"
|
|
|
|
#include "llvm/IR/Instructions.h"
|
|
|
|
#include "llvm/IR/LLVMContext.h"
|
|
|
|
#include "llvm/IR/Module.h"
|
|
|
|
#include "llvm/Support/SourceMgr.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
TEST(CaptureTracking, MaxUsesToExplore) {
|
|
|
|
StringRef Assembly = R"(
|
|
|
|
; Function Attrs: nounwind ssp uwtable
|
|
|
|
declare void @doesnt_capture(i8* nocapture, i8* nocapture, i8* nocapture,
|
|
|
|
i8* nocapture, i8* nocapture)
|
|
|
|
|
|
|
|
; %arg has 5 uses
|
|
|
|
define void @test_few_uses(i8* %arg) {
|
|
|
|
call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)
|
|
|
|
ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
; %arg has 50 uses
|
|
|
|
define void @test_many_uses(i8* %arg) {
|
|
|
|
call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)
|
|
|
|
call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)
|
|
|
|
call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)
|
|
|
|
call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)
|
|
|
|
call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)
|
|
|
|
call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)
|
|
|
|
call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)
|
|
|
|
call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)
|
|
|
|
call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)
|
|
|
|
call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)
|
|
|
|
ret void
|
|
|
|
}
|
|
|
|
)";
|
|
|
|
|
|
|
|
LLVMContext Context;
|
|
|
|
SMDiagnostic Error;
|
|
|
|
auto M = parseAssemblyString(Assembly, Error, Context);
|
|
|
|
ASSERT_TRUE(M) << "Bad assembly?";
|
|
|
|
|
|
|
|
auto Test = [&M](const char *FName, unsigned FalseMaxUsesLimit,
|
|
|
|
unsigned TrueMaxUsesLimit) {
|
|
|
|
Function *F = M->getFunction(FName);
|
|
|
|
ASSERT_NE(F, nullptr);
|
|
|
|
Value *Arg = &*F->arg_begin();
|
|
|
|
ASSERT_NE(Arg, nullptr);
|
|
|
|
ASSERT_FALSE(PointerMayBeCaptured(Arg, true, true, FalseMaxUsesLimit));
|
|
|
|
ASSERT_TRUE(PointerMayBeCaptured(Arg, true, true, TrueMaxUsesLimit));
|
|
|
|
|
|
|
|
BasicBlock *EntryBB = &F->getEntryBlock();
|
|
|
|
DominatorTree DT(*F);
|
|
|
|
|
|
|
|
Instruction *Ret = EntryBB->getTerminator();
|
|
|
|
ASSERT_TRUE(isa<ReturnInst>(Ret));
|
[IR] Lazily number instructions for local dominance queries
Essentially, fold OrderedBasicBlock into BasicBlock, and make it
auto-invalidate the instruction ordering when new instructions are
added. Notably, we don't need to invalidate it when removing
instructions, which is helpful when a pass mostly delete dead
instructions rather than transforming them.
The downside is that Instruction grows from 56 bytes to 64 bytes. The
resulting LLVM code is substantially simpler and automatically handles
invalidation, which makes me think that this is the right speed and size
tradeoff.
The important change is in SymbolTableTraitsImpl.h, where the numbering
is invalidated. Everything else should be straightforward.
We probably want to implement a fancier re-numbering scheme so that
local updates don't invalidate the ordering, but I plan for that to be
future work, maybe for someone else.
Reviewed By: lattner, vsk, fhahn, dexonsmith
Differential Revision: https://reviews.llvm.org/D51664
2020-02-19 06:33:54 +08:00
|
|
|
ASSERT_FALSE(PointerMayBeCapturedBefore(Arg, true, true, Ret, &DT, false,
|
|
|
|
FalseMaxUsesLimit));
|
2019-01-04 04:16:33 +08:00
|
|
|
ASSERT_TRUE(PointerMayBeCapturedBefore(Arg, true, true, Ret, &DT, false,
|
[IR] Lazily number instructions for local dominance queries
Essentially, fold OrderedBasicBlock into BasicBlock, and make it
auto-invalidate the instruction ordering when new instructions are
added. Notably, we don't need to invalidate it when removing
instructions, which is helpful when a pass mostly delete dead
instructions rather than transforming them.
The downside is that Instruction grows from 56 bytes to 64 bytes. The
resulting LLVM code is substantially simpler and automatically handles
invalidation, which makes me think that this is the right speed and size
tradeoff.
The important change is in SymbolTableTraitsImpl.h, where the numbering
is invalidated. Everything else should be straightforward.
We probably want to implement a fancier re-numbering scheme so that
local updates don't invalidate the ordering, but I plan for that to be
future work, maybe for someone else.
Reviewed By: lattner, vsk, fhahn, dexonsmith
Differential Revision: https://reviews.llvm.org/D51664
2020-02-19 06:33:54 +08:00
|
|
|
TrueMaxUsesLimit));
|
2019-01-04 04:16:33 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
Test("test_few_uses", 6, 4);
|
|
|
|
Test("test_many_uses", 50, 30);
|
|
|
|
}
|
2020-11-07 18:19:27 +08:00
|
|
|
|
|
|
|
struct CollectingCaptureTracker : public CaptureTracker {
|
|
|
|
SmallVector<const Use *, 4> Captures;
|
2020-11-07 18:44:02 +08:00
|
|
|
void tooManyUses() override { }
|
2020-11-07 18:19:27 +08:00
|
|
|
bool captured(const Use *U) override {
|
|
|
|
Captures.push_back(U);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST(CaptureTracking, MultipleUsesInSameInstruction) {
|
|
|
|
StringRef Assembly = R"(
|
|
|
|
declare void @call(i8*, i8*, i8*)
|
|
|
|
|
|
|
|
define void @test(i8* %arg, i8** %ptr) {
|
|
|
|
call void @call(i8* %arg, i8* nocapture %arg, i8* %arg) [ "bundle"(i8* %arg) ]
|
|
|
|
cmpxchg i8** %ptr, i8* %arg, i8* %arg acq_rel monotonic
|
|
|
|
icmp eq i8* %arg, %arg
|
|
|
|
ret void
|
|
|
|
}
|
|
|
|
)";
|
|
|
|
|
|
|
|
LLVMContext Context;
|
|
|
|
SMDiagnostic Error;
|
|
|
|
auto M = parseAssemblyString(Assembly, Error, Context);
|
|
|
|
ASSERT_TRUE(M) << "Bad assembly?";
|
|
|
|
|
|
|
|
Function *F = M->getFunction("test");
|
|
|
|
Value *Arg = &*F->arg_begin();
|
|
|
|
BasicBlock *BB = &F->getEntryBlock();
|
|
|
|
Instruction *Call = &*BB->begin();
|
|
|
|
Instruction *CmpXChg = Call->getNextNode();
|
|
|
|
Instruction *ICmp = CmpXChg->getNextNode();
|
|
|
|
|
|
|
|
CollectingCaptureTracker CT;
|
|
|
|
PointerMayBeCaptured(Arg, &CT);
|
|
|
|
EXPECT_EQ(7u, CT.Captures.size());
|
|
|
|
// Call arg 1
|
|
|
|
EXPECT_EQ(Call, CT.Captures[0]->getUser());
|
|
|
|
EXPECT_EQ(0u, CT.Captures[0]->getOperandNo());
|
|
|
|
// Call arg 3
|
|
|
|
EXPECT_EQ(Call, CT.Captures[1]->getUser());
|
|
|
|
EXPECT_EQ(2u, CT.Captures[1]->getOperandNo());
|
|
|
|
// Operand bundle arg
|
|
|
|
EXPECT_EQ(Call, CT.Captures[2]->getUser());
|
|
|
|
EXPECT_EQ(3u, CT.Captures[2]->getOperandNo());
|
|
|
|
// Cmpxchg compare operand
|
|
|
|
EXPECT_EQ(CmpXChg, CT.Captures[3]->getUser());
|
|
|
|
EXPECT_EQ(1u, CT.Captures[3]->getOperandNo());
|
|
|
|
// Cmpxchg new value operand
|
|
|
|
EXPECT_EQ(CmpXChg, CT.Captures[4]->getUser());
|
|
|
|
EXPECT_EQ(2u, CT.Captures[4]->getOperandNo());
|
|
|
|
// ICmp first operand
|
|
|
|
EXPECT_EQ(ICmp, CT.Captures[5]->getUser());
|
|
|
|
EXPECT_EQ(0u, CT.Captures[5]->getOperandNo());
|
|
|
|
// ICmp second operand
|
|
|
|
EXPECT_EQ(ICmp, CT.Captures[6]->getUser());
|
|
|
|
EXPECT_EQ(1u, CT.Captures[6]->getOperandNo());
|
|
|
|
}
|