Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -294,6 +294,7 @@ void initializeRegionPrinterPass(PassRegistry&); void initializeRegionViewerPass(PassRegistry&); void initializeRegisterCoalescerPass(PassRegistry&); +void initializeRemoveGCRelocatesPass(PassRegistry&); void initializeRenameIndependentSubregsPass(PassRegistry&); void initializeResetMachineFunctionPass(PassRegistry &); void initializeReversePostOrderFunctionAttrsLegacyPassPass(PassRegistry&); Index: include/llvm/Transforms/Scalar.h =================================================================== --- include/llvm/Transforms/Scalar.h +++ include/llvm/Transforms/Scalar.h @@ -472,6 +472,13 @@ //===----------------------------------------------------------------------===// // +// RemoveGCRelocates - Remove GC relocates that have been inserted by +// RewriteStatepointsForGC. The resulting IR is incorrect, but this is useful +// for analysis +FunctionPass *createRemoveGCRelocatesPass(); + +//===----------------------------------------------------------------------===// +// // Float2Int - Demote floats to ints where possible. // FunctionPass *createFloat2IntPass(); Index: lib/Transforms/Utils/CMakeLists.txt =================================================================== --- lib/Transforms/Utils/CMakeLists.txt +++ lib/Transforms/Utils/CMakeLists.txt @@ -34,6 +34,7 @@ ModuleUtils.cpp NameAnonGlobals.cpp PromoteMemoryToRegister.cpp + RemoveGCRelocates.cpp SSAUpdater.cpp SanitizerStats.cpp SimplifyCFG.cpp Index: lib/Transforms/Utils/RemoveGCRelocates.cpp =================================================================== --- /dev/null +++ lib/Transforms/Utils/RemoveGCRelocates.cpp @@ -0,0 +1,103 @@ +//===- RemoveGCRelocates.cpp - Remove gc.relocates inserted by RewriteStatePoints===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a little utility pass that removes the gc.relocates inserted by +// RewriteStatepointsForGC. Note that the generated IR would be incorrect, +// but this is useful as a single pass in itself, for analysis of IR, without +// the GC.relocates. The statepoint and gc.result instrinsics would still be +// present. +//===----------------------------------------------------------------------===// + +#include "llvm/IR/Function.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Statepoint.h" +#include "llvm/IR/Type.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace { +struct RemoveGCRelocates : public FunctionPass { + static char ID; // Pass identification, replacement for typeid + RemoveGCRelocates() : FunctionPass(ID) { + initializeRemoveGCRelocatesPass(*PassRegistry::getPassRegistry()); + } + + void getAnalysisUsage(AnalysisUsage &Info) const override { + Info.setPreservesAll(); + } + + bool runOnFunction(Function &F) override; + + SmallVector GCRelocates; +}; +char RemoveGCRelocates::ID = 0; +} + +bool RemoveGCRelocates::runOnFunction(Function &F) { + + // Nothing to do for declarations. + if (F.isDeclaration() || F.empty()) + return false; + GCRelocates.clear(); + for (Instruction &I : instructions(F)) { + ImmutableCallSite GCRel = ImmutableCallSite(&I); + if (!isGCRelocate(GCRel)) + continue; + GCRelocates.push_back(cast(&I)); + + } + bool Changed = false; + // All gc.relocates are bound to a single statepoint token. The order of + // visiting gc.relocates for deletion does not matter. + for (GCRelocateInst *GCRel : GCRelocates) { + + // TODO: Handle gc.relocates in landing pads (these are not bound to the + // statepoint token). + if (!isStatepoint(GCRel->getOperand(0))) + continue; + Value *OrigPtr = GCRel->getDerivedPtr(); + + Instruction *ToBeReplaced = GCRel; + // All gc_relocates are i8 addrspace(1)* typed, and may be bitcasted to + // the correct type of the original pointer (if they are not the same). + // TODO: Handle gc.relocates that are of different type from original + // pointer, but all uses are the gc.relocate of i8 addrspace(1)* type. + if (GCRel->getType() != OrigPtr->getType()) { + if (GCRel->hasOneUse() && isa(GCRel->user_back())) + ToBeReplaced = GCRel->user_back(); + else + continue; + } + assert(ToBeReplaced->getType() == OrigPtr->getType() && + "Should have same types!"); + // Replace all uses and delete the relocate (and bitcast'ed version, if + // necessary). + ToBeReplaced->replaceAllUsesWith(OrigPtr); + if (GCRel != ToBeReplaced) { + ToBeReplaced->eraseFromParent(); + // Only the bitcast of the gc.relocate was deleted above, we also need + // to delete the gc.relocate itself. + GCRel->eraseFromParent(); + } else + ToBeReplaced->eraseFromParent(); + Changed = true; + } + return Changed; +} + +INITIALIZE_PASS(RemoveGCRelocates, "strip-gc-relocates", + "Strip gc.relocates inserted through RewriteStatepointsForGC", + true, false) +FunctionPass *llvm::createRemoveGCRelocatesPass() { + return new RemoveGCRelocates(); +} Index: lib/Transforms/Utils/Utils.cpp =================================================================== --- lib/Transforms/Utils/Utils.cpp +++ lib/Transforms/Utils/Utils.cpp @@ -35,6 +35,7 @@ initializeMetaRenamerPass(Registry); initializeMemorySSAWrapperPassPass(Registry); initializeMemorySSAPrinterLegacyPassPass(Registry); + initializeRemoveGCRelocatesPass(Registry); } /// LLVMInitializeTransformUtils - C binding for initializeTransformUtilsPasses. Index: test/Transforms/Util/strip-gc-relocates.ll =================================================================== --- /dev/null +++ test/Transforms/Util/strip-gc-relocates.ll @@ -0,0 +1,121 @@ +; RUN: opt -S -strip-gc-relocates < %s | FileCheck %s +; test utility/debugging pass which removes gc.relocates, inserted by -rewrite-statepoints-for-gc +declare void @use_obj32(i32 addrspace(1)*) "gc-leaf-function" + +declare void @g() +declare token @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...) +declare i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token, i32, i32) #0 +declare void @do_safepoint() + +declare i32 addrspace(1)* @new_instance() #1 + + +; Simple case: remove gc.relocate +define i32 addrspace(1)* @test1(i32 addrspace(1)* %arg) gc "statepoint-example" { +entry: +; CHECK-LABEL: test1 +; CHECK: gc.statepoint +; CHECK-NOT: gc.relocate +; CHECK: ret i32 addrspace(1)* %arg + %statepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @g, i32 0, i32 0, i32 0, i32 1, i32 100, i32 addrspace(1)* %arg) + %arg.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 8, i32 8) ; (%arg, %arg) + %arg.relocated.casted = bitcast i8 addrspace(1)* %arg.relocated to i32 addrspace(1)* + ret i32 addrspace(1)* %arg.relocated.casted +} + +; Remove gc.relocates in presence of nested relocates. +define void @test2(i32 addrspace(1)* %base) gc "statepoint-example" { +entry: +; CHECK-LABEL: test2 +; CHECK: statepoint +; CHECK-NOT: gc.relocate +; CHECK: call void @use_obj32(i32 addrspace(1)* %ptr.gep1) +; CHECK: call void @use_obj32(i32 addrspace(1)* %ptr.gep1) + %ptr.gep = getelementptr i32, i32 addrspace(1)* %base, i32 15 + %ptr.gep1 = getelementptr i32, i32 addrspace(1)* %ptr.gep, i32 15 + %statepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @do_safepoint, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %ptr.gep1, i32 addrspace(1)* %base) + %ptr.gep1.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 8, i32 7) ; (%base, %ptr.gep1) + %ptr.gep1.relocated.casted = bitcast i8 addrspace(1)* %ptr.gep1.relocated to i32 addrspace(1)* + %base.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 8, i32 8) ; (%base, %base) + %base.relocated.casted = bitcast i8 addrspace(1)* %base.relocated to i32 addrspace(1)* + call void @use_obj32(i32 addrspace(1)* %ptr.gep1.relocated.casted) + %statepoint_token1 = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @do_safepoint, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %ptr.gep1.relocated.casted, i32 addrspace(1)* %base.relocated.casted) + %ptr.gep1.relocated2 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token1, i32 8, i32 7) ; (%base.relocated.casted, %ptr.gep1.relocated.casted) + %ptr.gep1.relocated2.casted = bitcast i8 addrspace(1)* %ptr.gep1.relocated2 to i32 addrspace(1)* + %base.relocated3 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token1, i32 8, i32 8) ; (%base.relocated.casted, %base.relocated.casted) + %base.relocated3.casted = bitcast i8 addrspace(1)* %base.relocated3 to i32 addrspace(1)* + call void @use_obj32(i32 addrspace(1)* %ptr.gep1.relocated2.casted) + ret void +} + +; TODO: remove gc.relocates from landing pad +define i32 addrspace(1)* @test3(i32 addrspace(1)* %arg) gc "statepoint-example" personality i32 8 { +; CHECK-LABEL: test3( +; CHECK: gc.statepoint +; CHECK-LABEL: normal_dest: +; CHECK-NOT: gc.relocate +; CHECK: ret i32 addrspace(1)* %arg +; CHECK-LABEL: unwind_dest: +; CHECK: gc.relocate +entry: + %statepoint_token = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @g, i32 0, i32 0, i32 0, i32 1, i32 100, i32 addrspace(1)* %arg) + to label %normal_dest unwind label %unwind_dest + +normal_dest: ; preds = %entry + %arg.relocated1 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 8, i32 8) ; (%arg, %arg) + %arg.relocated1.casted = bitcast i8 addrspace(1)* %arg.relocated1 to i32 addrspace(1)* + ret i32 addrspace(1)* %arg.relocated1.casted + +unwind_dest: ; preds = %entry + %lpad = landingpad token + cleanup + %arg.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %lpad, i32 8, i32 8) ; (%arg, %arg) + %arg.relocated.casted = bitcast i8 addrspace(1)* %arg.relocated to i32 addrspace(1)* + resume token undef +} + +; in presence of phi +define void @test4(i1 %cond) gc "statepoint-example" { +; CHECK-LABEL: test4 +entry: + %base1 = call i32 addrspace(1)* @new_instance() + %base2 = call i32 addrspace(1)* @new_instance() + br i1 %cond, label %here, label %there + +here: ; preds = %entry + br label %merge + +there: ; preds = %entry + br label %merge + +merge: ; preds = %there, %here +; CHECK-LABEL: merge: +; CHECK-NOT: gc.relocate +; CHECK: %ptr.gep.remat = getelementptr i32, i32 addrspace(1)* %basephi.base, i32 15 + %basephi.base = phi i32 addrspace(1)* [ %base1, %here ], [ %base2, %there ], !is_base_value !0 + %basephi = phi i32 addrspace(1)* [ %base1, %here ], [ %base2, %there ] + %ptr.gep = getelementptr i32, i32 addrspace(1)* %basephi, i32 15 + %statepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @do_safepoint, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %basephi.base) + %basephi.base.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 7, i32 7) ; (%basephi.base, %basephi.base) + %basephi.base.relocated.casted = bitcast i8 addrspace(1)* %basephi.base.relocated to i32 addrspace(1)* + %ptr.gep.remat = getelementptr i32, i32 addrspace(1)* %basephi.base.relocated.casted, i32 15 + call void @use_obj32(i32 addrspace(1)* %ptr.gep.remat) + ret void +} + +; The gc.relocate type is different from %arg, but removing the gc.relocate, +; will need a bitcast to be added from i32 addrspace(1)* to i8 addrspace(1)* +define i8 addrspace(1)* @test5(i32 addrspace(1)* %arg) gc "statepoint-example" { +entry: +; CHECK-LABEL: test5 +; CHECK: gc.statepoint +; CHECK: gc.relocate +; CHECK: ret i8 addrspace(1)* %arg.relocated + %statepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @g, i32 0, i32 0, i32 0, i32 1, i32 100, i32 addrspace(1)* %arg) + %arg.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 8, i32 8) ; (%arg, %arg) + ret i8 addrspace(1)* %arg.relocated +} + +attributes #0 = { nounwind readonly } +attributes #1 = { nounwind "gc-leaf-function" } +!0 = !{}