mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-08-24 09:29:42 +00:00
[RewriteStatepointsForGC] Strip deref info after rewriting.
Summary: Once a gc.statepoint has been rewritten to relocate live references, the SSA values represent physical pointers instead of logical references. Logical dereferencability does not imply physical dereferencability and after RewriteStatepointsForGC has run any attributes that imply dereferencability of the logical references need to be stripped. This current approach is conservative, and can be made more precise later if needed. For starters, we need to strip dereferencable attributes only from pointers that live in the GC address space. Reviewers: reames, pgavlin Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D10105 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@238883 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
e9609875ee
commit
f73780ec1a
@ -30,6 +30,7 @@
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/MDBuilder.h"
|
||||
#include "llvm/IR/Statepoint.h"
|
||||
#include "llvm/IR/Value.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
@ -85,6 +86,14 @@ struct RewriteStatepointsForGC : public ModulePass {
|
||||
bool Changed = false;
|
||||
for (Function &F : M)
|
||||
Changed |= runOnFunction(F);
|
||||
|
||||
if (Changed) {
|
||||
// stripDereferenceabilityInfo asserts that shouldRewriteStatepointsIn
|
||||
// returns true for at least one function in the module. Since at least
|
||||
// one function changed, we know that the precondition is satisfied.
|
||||
stripDereferenceabilityInfo(M);
|
||||
}
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
@ -94,6 +103,20 @@ struct RewriteStatepointsForGC : public ModulePass {
|
||||
AU.addRequired<DominatorTreeWrapperPass>();
|
||||
AU.addRequired<TargetTransformInfoWrapperPass>();
|
||||
}
|
||||
|
||||
/// The IR fed into RewriteStatepointsForGC may have had attributes implying
|
||||
/// dereferenceability that are no longer valid/correct after
|
||||
/// RewriteStatepointsForGC has run. This is because semantically, after
|
||||
/// RewriteStatepointsForGC runs, all calls to gc.statepoint "free" the entire
|
||||
/// heap. stripDereferenceabilityInfo (conservatively) restores correctness
|
||||
/// by erasing all attributes in the module that externally imply
|
||||
/// dereferenceability.
|
||||
///
|
||||
void stripDereferenceabilityInfo(Module &M);
|
||||
|
||||
// Helpers for stripDereferenceabilityInfo
|
||||
void stripDereferenceabilityInfoFromBody(Function &F);
|
||||
void stripDereferenceabilityInfoFromPrototype(Function &F);
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -2200,6 +2223,72 @@ static bool insertParsePoints(Function &F, DominatorTree &DT, Pass *P,
|
||||
return !records.empty();
|
||||
}
|
||||
|
||||
// Handles both return values and arguments for Functions and CallSites.
|
||||
template <typename AttrHolder>
|
||||
static void RemoveDerefAttrAtIndex(LLVMContext &Ctx, AttrHolder &AH,
|
||||
unsigned Index) {
|
||||
AttrBuilder R;
|
||||
if (AH.getDereferenceableBytes(Index))
|
||||
R.addAttribute(Attribute::get(Ctx, Attribute::Dereferenceable,
|
||||
AH.getDereferenceableBytes(Index)));
|
||||
if (AH.getDereferenceableOrNullBytes(Index))
|
||||
R.addAttribute(Attribute::get(Ctx, Attribute::DereferenceableOrNull,
|
||||
AH.getDereferenceableOrNullBytes(Index)));
|
||||
|
||||
if (!R.empty())
|
||||
AH.setAttributes(AH.getAttributes().removeAttributes(
|
||||
Ctx, Index, AttributeSet::get(Ctx, Index, R)));
|
||||
};
|
||||
|
||||
void
|
||||
RewriteStatepointsForGC::stripDereferenceabilityInfoFromPrototype(Function &F) {
|
||||
LLVMContext &Ctx = F.getContext();
|
||||
|
||||
for (Argument &A : F.args())
|
||||
if (isa<PointerType>(A.getType()))
|
||||
RemoveDerefAttrAtIndex(Ctx, F, A.getArgNo() + 1);
|
||||
|
||||
if (isa<PointerType>(F.getReturnType()))
|
||||
RemoveDerefAttrAtIndex(Ctx, F, AttributeSet::ReturnIndex);
|
||||
}
|
||||
|
||||
void RewriteStatepointsForGC::stripDereferenceabilityInfoFromBody(Function &F) {
|
||||
if (F.empty())
|
||||
return;
|
||||
|
||||
LLVMContext &Ctx = F.getContext();
|
||||
MDBuilder Builder(Ctx);
|
||||
|
||||
for (Instruction &I : inst_range(F)) {
|
||||
if (const MDNode *MD = I.getMetadata(LLVMContext::MD_tbaa)) {
|
||||
assert(MD->getNumOperands() < 5 && "unrecognized metadata shape!");
|
||||
bool IsImmutableTBAA =
|
||||
MD->getNumOperands() == 4 &&
|
||||
mdconst::extract<ConstantInt>(MD->getOperand(3))->getValue() == 1;
|
||||
|
||||
if (!IsImmutableTBAA)
|
||||
continue; // no work to do, MD_tbaa is already marked mutable
|
||||
|
||||
MDNode *Base = cast<MDNode>(MD->getOperand(0));
|
||||
MDNode *Access = cast<MDNode>(MD->getOperand(1));
|
||||
uint64_t Offset =
|
||||
mdconst::extract<ConstantInt>(MD->getOperand(2))->getZExtValue();
|
||||
|
||||
MDNode *MutableTBAA =
|
||||
Builder.createTBAAStructTagNode(Base, Access, Offset);
|
||||
I.setMetadata(LLVMContext::MD_tbaa, MutableTBAA);
|
||||
}
|
||||
|
||||
if (CallSite CS = CallSite(&I)) {
|
||||
for (int i = 0, e = CS.arg_size(); i != e; i++)
|
||||
if (isa<PointerType>(CS.getArgument(i)->getType()))
|
||||
RemoveDerefAttrAtIndex(Ctx, CS, i + 1);
|
||||
if (isa<PointerType>(CS.getType()))
|
||||
RemoveDerefAttrAtIndex(Ctx, CS, AttributeSet::ReturnIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this function should be rewritten by this pass. The main
|
||||
/// point of this function is as an extension point for custom logic.
|
||||
static bool shouldRewriteStatepointsIn(Function &F) {
|
||||
@ -2214,6 +2303,19 @@ static bool shouldRewriteStatepointsIn(Function &F) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RewriteStatepointsForGC::stripDereferenceabilityInfo(Module &M) {
|
||||
#ifndef NDEBUG
|
||||
assert(std::any_of(M.begin(), M.end(), shouldRewriteStatepointsIn) &&
|
||||
"precondition!");
|
||||
#endif
|
||||
|
||||
for (Function &F : M)
|
||||
stripDereferenceabilityInfoFromPrototype(F);
|
||||
|
||||
for (Function &F : M)
|
||||
stripDereferenceabilityInfoFromBody(F);
|
||||
}
|
||||
|
||||
bool RewriteStatepointsForGC::runOnFunction(Function &F) {
|
||||
// Nothing to do for declarations.
|
||||
if (F.isDeclaration() || F.empty())
|
||||
|
77
test/Transforms/RewriteStatepointsForGC/deref-pointers.ll
Normal file
77
test/Transforms/RewriteStatepointsForGC/deref-pointers.ll
Normal file
@ -0,0 +1,77 @@
|
||||
; RUN: opt -S -rewrite-statepoints-for-gc < %s | FileCheck %s
|
||||
|
||||
declare void @foo()
|
||||
declare i8 addrspace(1)* @some_function()
|
||||
declare void @some_function_consumer(i8 addrspace(1)*)
|
||||
declare dereferenceable(4) i8 addrspace(1)* @some_function_ret_deref()
|
||||
; CHECK: declare i8 addrspace(1)* @some_function_ret_deref()
|
||||
|
||||
define i8 addrspace(1)* @test_deref_arg(i8 addrspace(1)* dereferenceable(4) %a) gc "statepoint-example" {
|
||||
; CHECK: define i8 addrspace(1)* @test_deref_arg(i8 addrspace(1)* %a)
|
||||
entry:
|
||||
call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @foo, i32 0, i32 0, i32 0, i32 5, i32 0, i32 -1, i32 0, i32 0, i32 0)
|
||||
ret i8 addrspace(1)* %a
|
||||
}
|
||||
|
||||
define i8 addrspace(1)* @test_deref_or_null_arg(i8 addrspace(1)* dereferenceable_or_null(4) %a) gc "statepoint-example" {
|
||||
; CHECK: define i8 addrspace(1)* @test_deref_or_null_arg(i8 addrspace(1)* %a)
|
||||
entry:
|
||||
call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @foo, i32 0, i32 0, i32 0, i32 5, i32 0, i32 -1, i32 0, i32 0, i32 0)
|
||||
ret i8 addrspace(1)* %a
|
||||
}
|
||||
|
||||
define i8 addrspace(1)* @test_deref_retval() gc "statepoint-example" {
|
||||
; CHECK-LABEL: @test_deref_retval(
|
||||
entry:
|
||||
%a = call dereferenceable(4) i8 addrspace(1)* @some_function()
|
||||
; CHECK: %a = call i8 addrspace(1)* @some_function()
|
||||
call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @foo, i32 0, i32 0, i32 0, i32 5, i32 0, i32 -1, i32 0, i32 0, i32 0)
|
||||
ret i8 addrspace(1)* %a
|
||||
}
|
||||
|
||||
define i8 addrspace(1)* @test_deref_or_null_retval() gc "statepoint-example" {
|
||||
; CHECK-LABEL: @test_deref_or_null_retval(
|
||||
entry:
|
||||
%a = call dereferenceable_or_null(4) i8 addrspace(1)* @some_function()
|
||||
; CHECK: %a = call i8 addrspace(1)* @some_function()
|
||||
call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @foo, i32 0, i32 0, i32 0, i32 5, i32 0, i32 -1, i32 0, i32 0, i32 0)
|
||||
ret i8 addrspace(1)* %a
|
||||
}
|
||||
|
||||
define i8 @test_md(i8 addrspace(1)* %ptr) gc "statepoint-example" {
|
||||
; CHECK-LABEL: @test_md(
|
||||
entry:
|
||||
; CHECK: %tmp = load i8, i8 addrspace(1)* %ptr, !tbaa !0
|
||||
%tmp = load i8, i8 addrspace(1)* %ptr, !tbaa !0
|
||||
call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @foo, i32 0, i32 0, i32 0, i32 5, i32 0, i32 -1, i32 0, i32 0, i32 0)
|
||||
ret i8 %tmp
|
||||
}
|
||||
|
||||
define i8 addrspace(1)* @test_decl_only_attribute(i8 addrspace(1)* %ptr) gc "statepoint-example" {
|
||||
; CHECK-LABEL: @test_decl_only_attribute(
|
||||
entry:
|
||||
; No change here, but the prototype of some_function_ret_deref should have changed.
|
||||
; CHECK: call i8 addrspace(1)* @some_function_ret_deref()
|
||||
%a = call i8 addrspace(1)* @some_function_ret_deref()
|
||||
call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @foo, i32 0, i32 0, i32 0, i32 5, i32 0, i32 -1, i32 0, i32 0, i32 0)
|
||||
ret i8 addrspace(1)* %a
|
||||
}
|
||||
|
||||
define i8 addrspace(1)* @test_callsite_arg_attribute(i8 addrspace(1)* %ptr) gc "statepoint-example" {
|
||||
; CHECK-LABEL: @test_callsite_arg_attribute(
|
||||
entry:
|
||||
; CHECK: call void @some_function_consumer(i8 addrspace(1)* %ptr)
|
||||
call void @some_function_consumer(i8 addrspace(1)* dereferenceable(4) %ptr)
|
||||
call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @foo, i32 0, i32 0, i32 0, i32 5, i32 0, i32 -1, i32 0, i32 0, i32 0)
|
||||
ret i8 addrspace(1)* %ptr
|
||||
}
|
||||
|
||||
declare i32 @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...)
|
||||
|
||||
!0 = !{!1, !1, i64 0, i64 1}
|
||||
!1 = !{!"red", !2}
|
||||
!2 = !{!"blue"}
|
||||
|
||||
; CHECK: !0 = !{!1, !1, i64 0}
|
||||
; CHECK: !1 = !{!"red", !2}
|
||||
; CHECK: !2 = !{!"blue"}
|
Loading…
Reference in New Issue
Block a user