Wrong scope in !dbg on a branch (or: How to find BranchInst::Create?)

I’ve been trying to track down a redundant debug line-table entry, and it looks to me like a branch under an ‘if’ statement ends up with the wrong scope.

Here’s my example:

float distance = 1.0f;

int main(void)
{
    if (distance > 0.01f)
        return 1;

    return 0;
}

The thing to notice is that there are two scopes here: there’s the function scope, and then there’s a scope for the ‘if’ statement. Nothing exciting happens in this particular ‘if’ statement but the scope is still there. The problem comes with the IR:

  %0 = load float, ptr @distance, align 4, !dbg !18 ; block scope
  %cmp = fcmp ogt float %0, 0x3F847AE140000000, !dbg !20 ; block scope
  br i1 %cmp, label %if.then, label %if.end, !dbg !21 ; *** function scope ***

if.then:                                          ; preds = %entry
  store i32 1, ptr %retval, align 4, !dbg !22 ; block scope
  br label %return, !dbg !22

if.end:                                           ; preds = %entry
  store i32 0, ptr %retval, align 4, !dbg !23 ; function scope
  br label %return, !dbg !23

!14 = distinct !DISubprogram(... the usual stuff ...)
!18 = !DILocation(line: 4, column: 7, scope: !19)
!19 = distinct !DILexicalBlock(scope: !14, file: !3, line: 4, column: 7)
!20 = !DILocation(line: 4, column: 16, scope: !19)
!21 = !DILocation(line: 4, column: 7, scope: !14)
!22 = !DILocation(line: 5, column: 5, scope: !19)
!23 = !DILocation(line: 6, column: 3, scope: !14)

The conditional branch does not have ‘if’-statement scope, it has function scope.

Well, so what? Well, that difference in scope tracks all the way down into LLVM code generation, where it causes a redundant line-table entry:

.Ltmp0:
        .loc    0 4 7 prologue_end              # tc13837.cpp:4:7
        movss   distance(%rip), %xmm0           # xmm0 = mem[0],zero,zero,zero
.Ltmp1:
        .loc    0 4 7 is_stmt 0                 # tc13837.cpp:4:7 # *** redundant ***
        movss   .LCPI0_0(%rip), %xmm1           # xmm1 = [9.99999977E-3,0.0E+0,0.0E+0,0.0E+0]
.Ltmp2:
        .loc    0 4 16                          # tc13837.cpp:4:16
        ucomiss %xmm1, %xmm0
.Ltmp3:
        .loc    0 4 7                           # tc13837.cpp:4:7
        jbe     .LBB0_2

If I hack the scope in the IR metadata, that extra .loc directive goes away.

I’ve been trying to track down where the incorrect dbg metadata gets attached. It’s been easy enough to find CodeGenFunction::EmitIfStmt, which uses CodeGenFunction::EmitBranchOnBoolExpr to do the heavy lifting, which then ends with
Builder.CreateCondBr(CondV, TrueBlock, FalseBlock, Weights, Unpredictable);
which in turn is basically a call to BranchInst::Create, which… I cannot find.

If someone can tell me where this method is hiding, or (even better) what is likely to be the source of the incorrect scope, that would be lovely.

(The issue is very likely in Clang, not LLVM, but it won’t be possible to figure that out until I can see how the branch gets created and where the !dbg record comes from.)

Sorry if I got something wrong here, but this is how I think it works:

The debug location metadata is added to the instruction when IRBuilder::Insert is called in the IRBuilder (using IRBulder::AddMetadataToInst). And Insert is called by CreateCondBr.

Which debug location to use is normally prepared by for example calling SetCurrentDebugLocation prior to creating the instructions (this can also happen when setting the insertion point etc).

In clang codegen there are also convenience helpers such as ApplyDebugLocation which can be used to set a debug location for a specific scope in the compiler source code (i.e. the lifetime of the ApplyDebugLocation object). So when you see things like this

    {
      ApplyDebugLocation DL(*this, Cond);
      EmitBranchOnBoolExpr(CondOp->getLHS(), TrueBlock, FalseBlock,
                           LHSScaledTrueCount, LH, CondOp);
    }

then the ApplyDebugLocation c’tor will magically update the IRBuilder associated with the CodeGenFunctions to use the debug location associated with Cond. And then the d’tor for ApplyDebugLocation will restore the original location.

In other words. I think there should be something that prepares the debug location in the IRBuilder before the Builder.CreateCondBr, and that is what decides which location the branch is getting.

I figured there was something like that going on. I’ll try attacking it from that angle, thanks!