Index: llvm/trunk/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp =================================================================== --- llvm/trunk/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp +++ llvm/trunk/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp @@ -2452,6 +2452,37 @@ FoldSingleEntryPHINodes(&BB); } + // Before we start introducing relocations, we want to tweak the IR a bit to + // avoid unfortunate code generation effects. The main example is that we + // want to try to make sure the comparison feeding a branch is after any + // safepoints. Otherwise, we end up with a comparison of pre-relocation + // values feeding a branch after relocation. This is semantically correct, + // but results in extra register pressure since both the pre-relocation and + // post-relocation copies must be available in registers. For code without + // relocations this is handled elsewhere, but teaching the scheduler to + // reverse the transform we're about to do would be slightly complex. + // Note: This may extend the live range of the inputs to the icmp and thus + // increase the liveset of any statepoint we move over. This is profitable + // as long as all statepoints are in rare blocks. If we had in-register + // lowering for live values this would be a much safer transform. + auto getConditionInst = [](TerminatorInst *TI) -> Instruction* { + if (auto *BI = dyn_cast(TI)) + if (BI->isConditional()) + return dyn_cast(BI->getCondition()); + // TODO: Extend this to handle switches + return nullptr; + }; + for (BasicBlock &BB : F) { + TerminatorInst *TI = BB.getTerminator(); + if (auto *Cond = getConditionInst(TI)) + // TODO: Handle more than just ICmps here. We should be able to move + // most instructions without side effects or memory access. + if (isa(Cond) && Cond->hasOneUse()) { + MadeChange = true; + Cond->moveBefore(TI); + } + } + MadeChange |= insertParsePoints(F, DT, this, ParsePointNeeded); return MadeChange; } Index: llvm/trunk/test/Transforms/RewriteStatepointsForGC/codegen-cond.ll =================================================================== --- llvm/trunk/test/Transforms/RewriteStatepointsForGC/codegen-cond.ll +++ llvm/trunk/test/Transforms/RewriteStatepointsForGC/codegen-cond.ll @@ -0,0 +1,74 @@ +; RUN: opt -rewrite-statepoints-for-gc -S < %s | FileCheck %s + +; A null test of a single value +define i1 @test(i8 addrspace(1)* %p, i1 %rare) gc "statepoint-example" { +; CHECK-LABEL: @test +entry: + %cond = icmp eq i8 addrspace(1)* %p, null + br i1 %rare, label %safepoint, label %continue, !prof !0 +safepoint: + call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @safepoint, i32 0, i32 0, i32 0, i32 0) + br label %continue +continue: +; CHECK-LABEL: continue: +; CHECK: phi +; CHECK-DAG: [ %p.relocated, %safepoint ] +; CHECK-DAG [ %p, %entry ] +; CHECK: %cond = icmp +; CHECK: br i1 %cond + br i1 %cond, label %taken, label %untaken +taken: + ret i1 true +untaken: + ret i1 false +} + +; Comparing two pointers +define i1 @test2(i8 addrspace(1)* %p, i8 addrspace(1)* %q, i1 %rare) + gc "statepoint-example" { +; CHECK-LABEL: @test2 +entry: + %cond = icmp eq i8 addrspace(1)* %p, %q + br i1 %rare, label %safepoint, label %continue, !prof !0 +safepoint: + call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @safepoint, i32 0, i32 0, i32 0, i32 0) + br label %continue +continue: +; CHECK-LABEL: continue: +; CHECK: phi +; CHECK-DAG: [ %q.relocated, %safepoint ] +; CHECK-DAG [ %q, %entry ] +; CHECK: phi +; CHECK-DAG: [ %p.relocated, %safepoint ] +; CHECK-DAG [ %p, %entry ] +; CHECK: %cond = icmp +; CHECK: br i1 %cond + br i1 %cond, label %taken, label %untaken +taken: + ret i1 true +untaken: + ret i1 false +} + +; Sanity check that nothing bad happens if already last instruction +; before terminator +define i1 @test3(i8 addrspace(1)* %p, i8 addrspace(1)* %q, i1 %rare) + gc "statepoint-example" { +; CHECK-LABEL: @test3 +entry: + call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @safepoint, i32 0, i32 0, i32 0, i32 0) +; CHECK: gc.statepoint +; CHECK: %cond = icmp +; CHECK: br i1 %cond + %cond = icmp eq i8 addrspace(1)* %p, %q + br i1 %cond, label %taken, label %untaken +taken: + ret i1 true +untaken: + ret i1 false +} + +declare void @safepoint() +declare i32 @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...) + +!0 = !{!"branch_weights", i32 1, i32 10000}