A question to the DWARF experts on symbol indirection

It would be great if some DWARF expert could weigh in on this, since my
understanding on DWARF is rudimentary.

Let's say I have a C function defined as

struct ab
{
int a;
int b;
};

void fooWithAb( struct ab *_param)
{
}

Is it possible to emit DWARF statements, so that in the debugger the parameter
_param is hidden and the visibility is a and b, without a _param-> prefix ?

Ciao
Nat!

Why I need this:
https://www.mulle-kybernetik.com/weblog/2015/mulle_objc_meta_call_convention.html

Hi Nat!,

Is it possible to emit DWARF statements, so that in the debugger the
parameter _param is hidden and the visibility is a and b, without a _param-> prefix ?

It's certainly possible in LLVM IR. @llvm.dbg.declare and so on can
associate arbitrary Values in a function with whatever name you want.

But it looks like you're trying to convince Clang itself to do that.
That's likely to be harder since it's not exactly a natural C mapping;
I suspect a lot depends on just where you're doing your ABI lowering.
Either way you're probably more likely to get a good response if you
ask the question on the cfe-dev mailing list.

Cheers.

Tim.

From: llvm-dev [mailto:llvm-dev-bounces@lists.llvm.org] On Behalf Of Tim
Northover via llvm-dev
Sent: Wednesday, July 25, 2018 3:07 AM
To: Nat!
Cc: LLVM Developers Mailing List
Subject: Re: [llvm-dev] A question to the DWARF experts on symbol
indirection

Hi Nat!,

> Is it possible to emit DWARF statements, so that in the debugger the
> parameter _param is hidden and the visibility is a and b, without a
_param-> prefix ?

It's certainly possible in LLVM IR. @llvm.dbg.declare and so on can
associate arbitrary Values in a function with whatever name you want.

DWARF expressions are certainly powerful enough to describe this.

But it looks like you're trying to convince Clang itself to do that.
That's likely to be harder since it's not exactly a natural C mapping;
I suspect a lot depends on just where you're doing your ABI lowering.

Also depends on whether you want your debugger to be able to call these
functions.

My guess is that you want to describe the formal parameter as artificial,
using the proper struct description, so that calls work correctly.
Then create local variables whose storage locations work indirectly off
the (described as artificial) pointer parameter.

Whether you get your modified front-end to do this, or hack up the
backend, up to you. The question was *can* it be done, and the answer
to that is clearly yes.
--paulr

Hi Nat!,

Is it possible to emit DWARF statements, so that in the debugger the
parameter _param is hidden and the visibility is a and b, without a _param-> prefix ?

It's certainly possible in LLVM IR. @llvm.dbg.declare and so on can
associate arbitrary Values in a function with whatever name you want.

Thanks for this great tip! I tried this with some C derived and hacked IR code and
the results are promising. The debugger finds the value (but can't print it,
because it's not a legal C expression). But that should be OK, once my
clang compiler is used to JIT the expression, or so I hope.

But it looks like you're trying to convince Clang itself to do that.
That's likely to be harder since it's not exactly a natural C mapping;

That part is fortunately already working.

Ciao
Nat!

On 25.07.2018 15:41, paul.robinson@sony.com wrote:Is it possible to emit DWARF statements, so that in the debugger the

parameter _param is hidden and the visibility is a and b, without a

_param-> prefix ?

It's certainly possible in LLVM IR. @llvm.dbg.declare and so on can
associate arbitrary Values in a function with whatever name you want.

DWARF expressions are certainly powerful enough to describe this.

I am not so sure about this now, after I tried this today and yesterday with limited success.
The main problem I think is, that_param-> points to a location, that isn't a fixed offset relative to the stack pointer.

When I run my IR code through llc, llc was unable to provide a location for the debugger. This might be more a limitation of llvm than of DWARF though.

Here is part of a DWARF dump with my two fake formal parameters. But as you can see the DW_AT_location is missing, which leads to problems in gdb and lldb:

  <2><74>: Abbrev Number: 6 (DW_TAG_formal_parameter)
     <75>   DW_AT_location    : 2 byte block: 91 68 (DW_OP_fbreg: -24)
     <78>   DW_AT_name        : (indirect string, offset: 0xfd): _param
     <7c>   DW_AT_type        : <0xf5>
     <80>   DW_AT_artificial  : 1
  <2><80>: Abbrev Number: 7 (DW_TAG_formal_parameter)
     <81>   DW_AT_name        : (indirect string, offset: 0x104): name
     <85>   DW_AT_decl_file   : 2
     <86>   DW_AT_decl_line   : 16
     <87>   DW_AT_type        : <0x11b>
     <8b>   DW_AT_artificial  : 1
  <2><8b>: Abbrev Number: 7 (DW_TAG_formal_parameter)
     <8c>   DW_AT_name        : (indirect string, offset: 0x112): version
     <90>   DW_AT_decl_file   : 2
     <91>   DW_AT_decl_line   : 17
     <92>   DW_AT_type        : <0x130>
     <96>   DW_AT_artificial  : 1

This information relates to this .ll snippet created from my compiler (see below for full glory IR):

   %_param.addr = alloca %"struct.Hello::p.printName:version:"*, align 8
   %0 = getelementptr inbounds %"struct.Hello::p.printName:version:", %"struct.Hello::p.printName:version:"* %_param,
i32 0, i32 0
   call void @llvm.dbg.declare(metadata i8** %0, metadata !32, metadata !DIExpression()), !dbg !34
   %1 = getelementptr inbounds %"struct.Hello::p.printName:version:", %"struct.Hello::p.printName:version:"* %_param,
i32 0, i32 1
   call void @llvm.dbg.declare(metadata i32* %1, metadata !33, metadata !DIExpression()), !dbg !35
!32 = !DILocalVariable(name: "name", arg: 4, scope: !21, file: !3, line: 22, type: !29)
!33 = !DILocalVariable(name: "version", arg: 5, scope: !21, file: !3, line: 23, type: !11)
!34 = !DILocation(line: 22, column: 29, scope: !21)
!35 = !DILocation(line: 23, column: 35, scope: !21)

I am not sure, if I can compute the DW_AT_location for llvm, but I guess that's probably what I will try to do next.

But it looks like you're trying to convince Clang itself to do that.
That's likely to be harder since it's not exactly a natural C mapping;
I suspect a lot depends on just where you're doing your ABI lowering.

Also depends on whether you want your debugger to be able to call these
functions.

Until recently I believed, that because lldb uses clang JIT to evaluate expressions, that when lldb uses my clang to evaluate "a", the code would be generated for "_param->a" and that lldb would access the correct value. I lost a little confidence in this though.

My guess is that you want to describe the formal parameter as artificial,
using the proper struct description, so that calls work correctly.
Then create local variables whose storage locations work indirectly off
the (described as artificial) pointer parameter.

If I create local storage locations, then wouldn't the debugger see stale copied information in cases where the function code modifies an argument ?

foo( int a)
{
    ++a;
}

is really

foo( struct { int a; } *_param)
{
    ++_param->a;
}

but with alloca:

foo( struct { int a; } *_param)
{
    int  a;

    a = _param->a;
    ++_param->a;
    // debug: p a gives ???
}

Seeing stale information would be bad.

Whether you get your modified front-end to do this, or hack up the
backend, up to you. The question was *can* it be done, and the answer
to that is clearly yes.
--paulr

Actually, currently I am doing both. :slight_smile:

Ciao
Nat!

Either way you're probably more likely to get a good response if you
ask the question on the cfe-dev mailing list.

Cheers.

Tim.
_______________________________________________
LLVM Developers mailing list
llvm-dev@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev

; ModuleID = 'reduced.m'
source_filename = "reduced.m"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

%struct.anon = type { i32, i32 }
%struct._mulle_objc_method = type { i32, i8*, i8*, i32, i8* }
%struct._mulle_objc_loadclass = type { i32, i8*, i32, i32, i8*, i32, i32, i32, %struct._mulle_objc_ivarlist*, %struct._mulle_objc_methodlist*, %struct._mulle_objc_methodlist*, %struct._mulle_objc_propertylist*, %struct._mulle_objc_protocollist*, i32*, i8* }
%struct._mulle_objc_ivarlist = type opaque
%struct._mulle_objc_methodlist = type opaque
%struct._mulle_objc_propertylist = type { i32, [0 x %struct._mulle_objc_property] }
%struct._mulle_objc_property = type { i32, i32, i8*, i8*, i32, i32, i32 }
%struct._mulle_objc_protocollist = type { %struct._mulle_objc_protocollist*, i64, [0 x %struct._mulle_objc_protocol] }
%struct._mulle_objc_protocol = type { i32, i8* }
%"struct.Hello::p.printName:version:" = type { i8*, i32 }

@__mulle_objc_objccompilerinfo = global %struct.anon { i32 12, i32 0 }, align 4, !dbg !0
@OBJC_METH_VAR_NAME_ = private unnamed_addr constant [19 x i8] c"printName:version:\00", align 1
@OBJC_METH_VAR_TYPE_ = private unnamed_addr constant [14 x i8] c"v28@0:8*16I24\00", align 1
@OBJC_CLASS_NAME_ = private unnamed_addr constant [6 x i8] c"Hello\00", align 1
@OBJC_CLASS_METHODS_Hello = private global { i32, i8*, [1 x %struct._mulle_objc_method] } { i32 1, i8* null, [1 x %struct._mulle_objc_method] [%struct._mulle_objc_method { i32 -1285604266, i8* getelementptr inbounds ([19 x i8], [19 x i8]* @OBJC_METH_VAR_NAME_, i32 0, i32 0), i8* getelementptr inbounds ([14 x i8], [14 x i8]* @OBJC_METH_VAR_TYPE_, i32 0, i32 0), i32 0, i8* bitcast (void (i8*, i32, %"struct.Hello::p.printName:version:"*)* @"+[Hello printName:version:]" to i8*) }] }, section "_DATA,__cls_meth,regular,no_dead_strip", align 4
@OBJC_DEBUG_INFO_ = private unnamed_addr constant [10 x i8] c"reduced.m\00", align 1
@OBJC_CLASS_Hello = private global %struct._mulle_objc_loadclass { i32 1438848191, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @OBJC_CLASS_NAME_, i32 0, i32 0), i32 298441816, i32 0, i8* null, i32 0, i32 -1, i32 0, %struct._mulle_objc_ivarlist* null, %struct._mulle_objc_methodlist* bitcast ({ i32, i8*, [1 x %struct._mulle_objc_method] }* @OBJC_CLASS_METHODS_Hello to %struct._mulle_objc_methodlist*), %struct._mulle_objc_methodlist* null, %struct._mulle_objc_propertylist* null, %struct._mulle_objc_protocollist* null, i32* null, i8* getelementptr inbounds ([10 x i8], [10 x i8]* @OBJC_DEBUG_INFO_, i32 0, i32 0) }, section "__DATA,__class,regular,no_dead_strip", align 4
@OBJC_HASHNAME_ = private global [1 x i8] zeroinitializer, section "__DATA,__module_info,regular,no_dead_strip", align 4
@OBJC_HASHNAME_Hello = private global [6 x i8] c"Hello\00", section "__DATA,__module_info,regular,no_dead_strip", align 4
@"OBJC_HASHNAME_printName:version:" = private global [19 x i8] c"printName:version:\00", section "__DATA,__module_info,regular,no_dead_strip", align 4
@llvm.compiler.used = appending global [9 x i8*] [i8* getelementptr inbounds ([19 x i8], [19 x i8]* @OBJC_METH_VAR_NAME_, i32 0, i32 0), i8* getelementptr inbounds ([14 x i8], [14 x i8]* @OBJC_METH_VAR_TYPE_, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @OBJC_CLASS_NAME_, i32 0, i32 0), i8* bitcast ({ i32, i8*, [1 x %struct._mulle_objc_method] }* @OBJC_CLASS_METHODS_Hello to i8*), i8* getelementptr inbounds ([10 x i8], [10 x i8]* @OBJC_DEBUG_INFO_, i32 0, i32 0), i8* bitcast (%struct._mulle_objc_loadclass* @OBJC_CLASS_Hello to i8*), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @OBJC_HASHNAME_, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @OBJC_HASHNAME_Hello, i32 0, i32 0), i8* getelementptr inbounds ([19 x i8], [19 x i8]* @"OBJC_HASHNAME_printName:version:", i32 0, i32 0)], section "llvm.metadata"

; Function Attrs: noinline nounwind optnone uwtable
define internal void @"+[Hello printName:version:]"(i8* nonnull %self, i32 zeroext %_cmd, %"struct.Hello::p.printName:version:"* %_param) #0 !dbg !21 {
entry:
   %self.addr = alloca i8*, align 8
   %_cmd.addr = alloca i32, align 4
   %_param.addr = alloca %"struct.Hello::p.printName:version:"*, align 8
   %0 = getelementptr inbounds %"struct.Hello::p.printName:version:", %"struct.Hello::p.printName:version:"* %_param, i32 0, i32 0
   call void @llvm.dbg.declare(metadata i8** %0, metadata !32, metadata !DIExpression()), !dbg !34
   %1 = getelementptr inbounds %"struct.Hello::p.printName:version:", %"struct.Hello::p.printName:version:"* %_param, i32 0, i32 1
   call void @llvm.dbg.declare(metadata i32* %1, metadata !33, metadata !DIExpression()), !dbg !35
   store i8* %self, i8** %self.addr, align 8
   call void @llvm.dbg.declare(metadata i8** %self.addr, metadata !36, metadata !DIExpression()), !dbg !38
   store i32 %_cmd, i32* %_cmd.addr, align 4
   call void @llvm.dbg.declare(metadata i32* %_cmd.addr, metadata !39, metadata !DIExpression()), !dbg !38
   store %"struct.Hello::p.printName:version:"* %_param, %"struct.Hello::p.printName:version:"** %_param.addr, align 8
   call void @llvm.dbg.declare(metadata %"struct.Hello::p.printName:version:"** %_param.addr, metadata !41, metadata !DIExpression()), !dbg !38
   ret void, !dbg !47
}

; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.declare(metadata, metadata, metadata) #1

attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone speculatable }

!llvm.module.flags = !{!13, !14, !15, !16, !17, !18, !19}
!llvm.dbg.cu = !{!2}
!llvm.ident = !{!20}

!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "__mulle_objc_objccompilerinfo", scope: !2, file: !3, line: 5, type: !8, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_ObjC, file: !3, producer: "mulle-clang 6.0.0.4 (runtime-load-version: 12) clang version 6.0.0  (based on LLVM 6.0.0)", isOptimized: false, runtimeVersion: 1848, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !7)
!3 = !DIFile(filename: "reduced.m", directory: "/tmp/yyy")
!4 = !{}
!5 = !{!6}
!6 = !DICompositeType(tag: DW_TAG_structure_type, name: "Hello", scope: !3, file: !3, line: 12, flags: DIFlagObjcClassComplete, elements: !4, runtimeLang: DW_LANG_ObjC)
!7 = !{!0}
!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !3, line: 1, size: 64, elements: !9)
!9 = !{!10, !12}
!10 = !DIDerivedType(tag: DW_TAG_member, name: "load", scope: !8, file: !3, line: 3, baseType: !11, size: 32)
!11 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
!12 = !DIDerivedType(tag: DW_TAG_member, name: "runtime", scope: !8, file: !3, line: 4, baseType: !11, size: 32, offset: 32)
!13 = !{i32 1, !"Objective-C Version", i32 1848}
!14 = !{i32 1, !"Objective-C Image Info Version", i32 0}
!15 = !{i32 1, !"Objective-C Image Info Section", !"__DATA, __image_info,regular,no_dead_strip"}
!16 = !{i32 4, !"Objective-C Garbage Collection", i32 0}
!17 = !{i32 2, !"Dwarf Version", i32 4}
!18 = !{i32 2, !"Debug Info Version", i32 3}
!19 = !{i32 1, !"wchar_size", i32 4}
!20 = !{!"mulle-clang 6.0.0.4 (runtime-load-version: 12) clang version 6.0.0  (based on LLVM 6.0.0)"}
!21 = distinct !DISubprogram(name: "+[Hello printName:version:]", scope: !3, file: !3, line: 22, type: !22, isLocal: true, isDefinition: true, scopeLine: 22, flags: DIFlagPrototyped, isOptimized: false, unit: !2, variables: !31)
!22 = !DISubroutineType(types: !23)
!23 = !{null, !24, !27, !29, !11}
!24 = !DIDerivedType(tag: DW_TAG_typedef, name: "Class", file: !3, line: 5, baseType: !25, flags: DIFlagArtificial | DIFlagObjectPointer)
!25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64)
!26 = !DICompositeType(tag: DW_TAG_structure_type, name: "objc_class", file: !3, flags: DIFlagFwdDecl)
!27 = !DIDerivedType(tag: DW_TAG_typedef, name: "SEL", file: !3, line: 5, baseType: !28, flags: DIFlagArtificial)
!28 = !DICompositeType(tag: DW_TAG_structure_type, name: "objc_selector", file: !3, flags: DIFlagFwdDecl)
!29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !30, size: 64)
!30 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
!31 = !{!32, !33}
!32 = !DILocalVariable(name: "name", arg: 4, scope: !21, file: !3, line: 22, type: !29)
!33 = !DILocalVariable(name: "version", arg: 5, scope: !21, file: !3, line: 23, type: !11)
!34 = !DILocation(line: 22, column: 29, scope: !21)
!35 = !DILocation(line: 23, column: 35, scope: !21)
!36 = !DILocalVariable(name: "self", arg: 1, scope: !21, type: !37, flags: DIFlagArtificial | DIFlagObjectPointer)
!37 = !DIDerivedType(tag: DW_TAG_typedef, name: "Class", file: !3, line: 5, baseType: !25)
!38 = !DILocation(line: 0, scope: !21)
!39 = !DILocalVariable(name: "_cmd", arg: 2, scope: !21, type: !40, flags: DIFlagArtificial)
!40 = !DIDerivedType(tag: DW_TAG_typedef, name: "SEL", file: !3, line: 5, baseType: !28)
!41 = !DILocalVariable(name: "_param", arg: 3, scope: !21, type: !42, flags: DIFlagArtificial)
!42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64)
!43 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "p.printName:version:", file: !3, line: 22, size: 128, elements: !44)
!44 = !{!45, !46}
!45 = !DIDerivedType(tag: DW_TAG_member, name: "name", scope: !43, file: !3, line: 22, baseType: !29, size: 64)
!46 = !DIDerivedType(tag: DW_TAG_member, name: "version", scope: !43, file: !3, line: 23, baseType: !11, size: 32, offset: 64)
!47 = !DILocation(line: 25, column: 1, scope: !21)

Original .m

struct
{
    unsigned int   load;
    unsigned int   runtime;
} __mulle_objc_objccompilerinfo =
{
    12,
    0
};

@interface Hello

+ (void) printName:(char *) name
            version:(unsigned int) version;

@end

@implementation Hello

+ (void) printName:(char *) name
            version:(unsigned int) version;
{
}

@end

+ Adrian in case he might have insight into the dbg.value question.

From: Nat! [mailto:nat@mulle-kybernetik.com]
Sent: Thursday, July 26, 2018 1:39 PM
To: Robinson, Paul; t.p.northover@gmail.com
Cc: llvm-dev@lists.llvm.org
Subject: Re: [llvm-dev] A question to the DWARF experts on symbol
indirection

On 25.07.2018 15:41, paul.robinson@sony.com wrote:Is it possible to emit
DWARF statements, so that in the debugger the
>>> parameter _param is hidden and the visibility is a and b, without a
>> _param-> prefix ?
>>
>> It's certainly possible in LLVM IR. @llvm.dbg.declare and so on can
>> associate arbitrary Values in a function with whatever name you want.
> DWARF expressions are certainly powerful enough to describe this.
I am not so sure about this now, after I tried this today and yesterday
with limited success.
The main problem I think is, that_param-> points to a location, that
isn't a fixed offset relative to the stack pointer.

When I run my IR code through llc, llc was unable to provide a location
for the debugger. This might be more a limitation of llvm than of DWARF
though.

I am pretty sure you are pushing the boundaries of what LLVM knows how
to do. DWARF itself knows how to access a value at (pointer + offset),
even if LLVM doesn't know how to generate such an expression (yet?).

(This is definitely beyond the boundaries of what I personally know how
to get LLVM to do. However I did pretty much exactly this in a previous
compiler I worked on, so the DWARF part is certainly feasible.)

Here is part of a DWARF dump with my two fake formal parameters. But as
you can see the DW_AT_location is missing, which leads to problems in
gdb and lldb:

Yes, that would be a problem. :slight_smile:

  <2><74>: Abbrev Number: 6 (DW_TAG_formal_parameter)
     <75>   DW_AT_location    : 2 byte block: 91 68 (DW_OP_fbreg: -24)
     <78>   DW_AT_name        : (indirect string, offset: 0xfd): _param
     <7c>   DW_AT_type        : <0xf5>
     <80>   DW_AT_artificial  : 1
  <2><80>: Abbrev Number: 7 (DW_TAG_formal_parameter)
     <81>   DW_AT_name        : (indirect string, offset: 0x104): name
     <85>   DW_AT_decl_file   : 2
     <86>   DW_AT_decl_line   : 16
     <87>   DW_AT_type        : <0x11b>
     <8b>   DW_AT_artificial  : 1
  <2><8b>: Abbrev Number: 7 (DW_TAG_formal_parameter)
     <8c>   DW_AT_name        : (indirect string, offset: 0x112): version
     <90>   DW_AT_decl_file   : 2
     <91>   DW_AT_decl_line   : 17
     <92>   DW_AT_type        : <0x130>
     <96>   DW_AT_artificial  : 1

This information relates to this .ll snippet created from my compiler
(see below for full glory IR):

   %_param.addr = alloca %"struct.Hello::p.printName:version:"*, align 8
   %0 = getelementptr inbounds %"struct.Hello::p.printName:version:",
%"struct.Hello::p.printName:version:"* %_param,
i32 0, i32 0
   call void @llvm.dbg.declare(metadata i8** %0, metadata !32, metadata
!DIExpression()), !dbg !34
   %1 = getelementptr inbounds %"struct.Hello::p.printName:version:",
%"struct.Hello::p.printName:version:"* %_param,
i32 0, i32 1
   call void @llvm.dbg.declare(metadata i32* %1, metadata !33, metadata
!DIExpression()), !dbg !35
!32 = !DILocalVariable(name: "name", arg: 4, scope: !21, file: !3, line:
22, type: !29)
!33 = !DILocalVariable(name: "version", arg: 5, scope: !21, file: !3,
line: 23, type: !11)
!34 = !DILocation(line: 22, column: 29, scope: !21)
!35 = !DILocation(line: 23, column: 35, scope: !21)

I am not sure, if I can compute the DW_AT_location for llvm, but I guess
that's probably what I will try to do next.

>> But it looks like you're trying to convince Clang itself to do that.
>> That's likely to be harder since it's not exactly a natural C mapping;
>> I suspect a lot depends on just where you're doing your ABI lowering.
> Also depends on whether you want your debugger to be able to call these
> functions.
Until recently I believed, that because lldb uses clang JIT to evaluate
expressions, that when lldb uses my clang to evaluate "a", the code
would be generated for "_param->a" and that lldb would access the
correct value. I lost a little confidence in this though.

>
> My guess is that you want to describe the formal parameter as
artificial,
> using the proper struct description, so that calls work correctly.
> Then create local variables whose storage locations work indirectly off
> the (described as artificial) pointer parameter.
If I create local storage locations, then wouldn't the debugger see
stale copied information in cases where the function code modifies an
argument ?

Sorry, I explained that poorly. I meant create local variables *in DWARF*
(or metadata, in the frontend) whose storage locations work indirectly off
the pointer. You seem to have been heading that direction with formal
parameters instead of variables, which is the same idea; whether it will
confuse the debugger is a different problem, but the concept is right.

I think Tim's point was that, given you have metadata for the individual
entities (params or vars) "a" and "b", you then need to emit an
llvm.dbg.value intrinsic that can tie the metadata to the indirect location.
If you do that, then the mechanism to translate that into a DWARF location
expression should kick in and emit DW_AT_location for you.

I honestly don't know whether LLVM can handle a location expression with
this degree of complexity. However, definitely there is interest in getting
it to handle that correctly, because handling more complex expressions is
key to improving the availability of variables in more-optimized code, and
there is a *lot* of interest in improving debugging of optimized code.

HTH,
--paulr