IRInterpreter strange behavior

I’m trying to diagnose a problem with the IRInterpreter, but this is all very foreign to me so I think I should ask here.

Before I go into the details though, a simple question which I don’t want to get lost in the shuffle: Why doesn’t LLDB use LLI instead of creating its own interpreter?

Anyway, on to the problem. First of all, this is only on Windows. This all just works on Linux and presumably Mac, although I don’t have a Mac to test. The scenario is this: I run the following two commands:

(lldb) expr const unsigned int $foo = 5
(lldb) expr &$foo

And this results in an error. Printing the value of $foo works fine, it just can’t get the address.

I turn on logging and I see this:

Module as passed in to IRInterpreter::Interpret:
"; ModuleID = ‘$__lldb_module’
target datalayout = “e-m:w-p:32:32-i64:64-f80:32-n8:16:32-S32”
target triple = “i686-pc-windows-msvc”

@"\01?$S1@?0??$__lldb_expr@@YAXPAX@Z@4IA" = internal global i32 0

; Function Attrs: nounwind
define void @"\01?$__lldb_expr@@YAXPAX@Z"(i8* %"$__lldb_arg") #0 {
%1 = getelementptr i8* %"$__lldb_arg", i32 8
%2 = bitcast i8* %1 to i32**
%3 = getelementptr i8* %"$__lldb_arg", i32 0
%4 = bitcast i8* %3 to i32***
%5 = load i32*** %4
%6 = alloca i8*, align 4, !clang.decl.ptr !8
store i8* %"$__lldb_arg", i8** %6, align 4
%7 = load i32* @"\01?$S1@?0??$__lldb_expr@@YAXPAX@Z@4IA"
%8 = and i32 %7, 1
%9 = icmp ne i32 %8, 0
br i1 %9, label %13, label %10

; :10 ; preds = %0
%11 = or i32 %7, 1
store i32 %11, i32* @"\01?$S1@?0??$__lldb_expr@@YAXPAX@Z@4IA"
%12 = load i32** %2, align 4
store i32* %12, i32** %5, align 4
br label %13

; :13 ; preds = %10, %0
ret void
}

attributes #0 = { nounwind “less-precise-fpmad”=“false” “no-frame-pointer-elim”=“true” “no-frame-pointer-elim-non-leaf” “no-infs-fp-math”=“false” “no-nans-fp-math”=“false” “no-realign-stack” “stack-protector-buffer-size”=“0” “unsafe-fp-math”=“false” “use-soft-float”=“false” }

!clang.global.decl.ptrs = !{!0, !1, !2, !0, !0}
!llvm.module.flags = !{!3, !4, !5, !6}
!llvm.ident = !{!7}

!0 = metadata !{null, i64 7787680}
!1 = metadata !{null, i64 7787504}
!2 = metadata !{void (i8*)* @"\01?$__lldb_expr@@YAXPAX@Z", i64 7787328}
!3 = metadata !{i32 1, metadata !“Objective-C Version”, i32 2}
!4 = metadata !{i32 1, metadata !“Objective-C Image Info Version”, i32 0}
!5 = metadata !{i32 1, metadata !“Objective-C Image Info Section”, metadata !"__DATA, __objc_imageinfo, regular, no_dead_strip"}
!6 = metadata !{i32 4, metadata !“Objective-C Garbage Collection”, i32 0}
!7 = metadata !{metadata !“clang version 3.5.0 “}
!8 = metadata !{i64 7787200}
"
IRMemoryMap::WriteMemory (0x81ffc, 0x124db54, 0x4) went to [0x2000…0x82000)
Made an allocation for argument i8* %”$__lldb_arg”
Data region : 1000
Ref region : 81ffc
Interpreting %1 = getelementptr i8* %"$__lldb_arg", i32 8
IRMemoryMap::WriteMemory (0x81ff8, 0x702b00, 0x4) went to [0x2000…0x82000)
Interpreted a GetElementPtrInst
P : i8* %"$__lldb_arg" 0x81ffc
Poffset : %1 = getelementptr i8* %"$__lldb_arg", i32 8 0x81ff8
Interpreting %2 = bitcast i8* %1 to i32**
IRMemoryMap::WriteMemory (0x81ff4, 0x702b00, 0x4) went to [0x2000…0x82000)
Interpreting %3 = getelementptr i8* %"$__lldb_arg", i32 0
IRMemoryMap::WriteMemory (0x81ff0, 0x702b00, 0x4) went to [0x2000…0x82000)
Interpreted a GetElementPtrInst
P : i8* %"$__lldb_arg" 0x81ffc
Poffset : %3 = getelementptr i8* %"$__lldb_arg", i32 0 0x81ff0
Interpreting %4 = bitcast i8* %3 to i32***
IRMemoryMap::WriteMemory (0x81fec, 0x702b00, 0x4) went to [0x2000…0x82000)
Interpreting %5 = load i32*** %4
IRMemoryMap::ReadMemory (0x81fec, 0x702b00, 0x4) came from [0x2000…0x82000)
IRMemoryMap::ReadMemory (0x1000, 0x702b00, 0x4) came from [0x1000…0x1010)
IRMemoryMap::WriteMemory (0x81fe8, 0x702b00, 0x4) went to [0x2000…0x82000)
Interpreted a LoadInst
P : 0x81fec
R : 0x1000
D : 0x81fe8
Interpreting %6 = alloca i8*, align 4, !clang.decl.ptr !8
IRMemoryMap::WriteMemory (0x81fe0, 0x124dc00, 0x4) went to [0x2000…0x82000)
Interpreted an AllocaInst
R : 0x81fe4
P : 0x81fe0
Interpreting store i8* %"$__lldb_arg", i8** %6, align 4
IRMemoryMap::ReadMemory (0x81fe0, 0x702b00, 0x4) came from [0x2000…0x82000)
IRMemoryMap::ReadMemory (0x81ffc, 0x702b00, 0x4) came from [0x2000…0x82000)
IRMemoryMap::WriteMemory (0x81fe4, 0x702b00, 0x4) went to [0x2000…0x82000)
Interpreted a StoreInst
D : 0x81ffc
P : 0x81fe0
R : 0x81fe4
Interpreting %7 = load i32* @"\01?$S1@?0??$__lldb_expr@@YAXPAX@Z@4IA"
LoadInst’s pointer doesn’t resolve to anything

I’ve bolded a few of the relevant lines. I’m not really sure what’s going on here, or even how to diagnose it. Can anyone offer some advice?

Thanks

Zachary,

LLI’s interpreter is (a) not really supported code, having been superseded by the JIT; and (b) founded on the assumption that we’re interpreting inside the current process.
The LLDB IRInterpreter is focused on interpreting in the context of another process (the IRMemoryMap is the interface it uses).

It’s not clear to me what’s doing this. On OS X we don’t get this error:

(lldb) expr const unsigned int $foo = 5
(lldb) expr &$foo
(const unsigned int *) $0 = 0x046cdbe26a5d1000

and the symbol isn’t there either:


; ModuleID = ‘$__lldb_module’
target datalayout = “e-m:o-i64:64-f80:128-n8:16:32:64-S128”
target triple = “x86_64-apple-macosx”

; Function Attrs: nounwind
define void @"_Z12$__lldb_exprPv"(i8* %"$__lldb_arg") #0 {
%1 = getelementptr i8* %"$__lldb_arg", i32 8
%2 = bitcast i8* %1 to i32**
%3 = getelementptr i8* %"$__lldb_arg", i32 0
%4 = bitcast i8* %3 to i32***
%5 = load i32*** %4
%6 = alloca i8*, align 8, !clang.decl.ptr !8
store i8* %"$__lldb_arg", i8** %6, align 8
%7 = icmp eq i8 0, 0
br i1 %7, label %8, label %10

; :8 ; preds = %0
%9 = load i32** %2, align 8
store i32* %9, i32** %5, align 8
br label %10

; :10 ; preds = %8, %0
ret void
}

What’s producing that? Is it some kind of guard variable? (IRForTarget.cpp strips those, but only the ones I’ve seen…)

Sean

Thanks, I think that was it. This was a guard variable as mangled by the Microsoft mangler.

Isn't the IRInterpreter also only for interpreting inside the current
process? At least the header file implies so.

//----------------------------------------------------------------------
/// @class IRInterpreter IRInterpreter.h "lldb/Expression/IRInterpreter.h"
/// @brief Attempt to interpret the function's code if it does not require
/// running the target.
///
/// In some cases, the IR for an expression can be evaluated entirely
/// in the debugger, manipulating variables but not executing any code
/// in the target. The IRInterpreter attempts to do this.
//----------------------------------------------------------------------

Of course LLDB itself also needs to interpret things inside the context of
the remote process, but I assumed that was handled by a different class.

Zachary,

LLI’s interpreter is (a) not really supported code, having been superseded by the JIT; and (b) founded on the assumption that we’re interpreting inside the current process.
The LLDB IRInterpreter is focused on interpreting in the context of another process (the IRMemoryMap is the interface it uses).

It’s not clear to me what’s doing this. On OS X we don’t get this error:

Isn't the IRInterpreter also only for interpreting inside the current process? At least the header file implies so.

//----------------------------------------------------------------------
/// @class IRInterpreter IRInterpreter.h "lldb/Expression/IRInterpreter.h"
/// @brief Attempt to interpret the function's code if it does not require
/// running the target.
///
/// In some cases, the IR for an expression can be evaluated entirely
/// in the debugger, manipulating variables but not executing any code
/// in the target. The IRInterpreter attempts to do this.
//----------------------------------------------------------------------

Of course LLDB itself also needs to interpret things inside the context of the remote process, but I assumed that was handled by a different class.

You are misunderstanding what "evaluated entirely in the debugger means". If the expression involves ONLY memory/register reads/writes to the remote process, then the debugger can emulate running the expression in the target process, without having to actually let the target run.

For instance, if the expression is just "local_variable = 5", we are perfectly capable of changing the value of local_variable in the target by a direct memory or register write. OTOH if an expression has a function call, we don't try to emulate all the code of the function call locally, instead we JIT the code to do so and run it in the target.

But we are still always resolving all the addresses in the IR as we interpret it in the target process.

There isn't any place in lldb where we do local code insertion or IR emulation to change our own state. We're not that cool...

Jim