NAME=simple type propagation
FILE=bins/mach0/simple-retype
CMDS=<<EOF
af
e asm.var.summary=0
aaft
afv
?e ---
pdf
EOF
EXPECT=<<EOF
var char * ptr @ sp+0x8
var int64_t var_10h @ sp+0x10
---
            ;-- section.0.__TEXT.__text:
            ;-- entry0:
            ;-- func.100003f68:
            ;-- pc:
/ 56: int main (int argc, char **argv, char **envp);
|           ; var char *ptr @ sp+0x8
|           ; var int64_t var_10h @ sp+0x10
|           0x100003f68      ff8300d1       sub sp, sp, 0x20           ; [00] -r-x section size 56 named 0.__TEXT.__text
|           0x100003f6c      fd7b01a9       stp x29, x30, [var_10h]
|           0x100003f70      fd430091       add x29, sp, 0x10
|           0x100003f74      08000090       adrp x8, 0x100003000
|           0x100003f78      08b13e91       add x8, x8, 0xfac
|           0x100003f7c      e80700f9       str x8, [ptr]
|           0x100003f80      e10740f9       ldr x1, [ptr]              ; const char *ptr
|           0x100003f84      20008052       mov w0, 1
|           0x100003f88      420180d2       mov x2, 0xa                ; size_t nbytes
|           0x100003f8c      05000094       bl sym.imp.write           ; ssize_t write(int fd, const char *ptr, size_t nbytes)
|           0x100003f90      00008052       mov w0, 0
|           0x100003f94      fd7b41a9       ldp x29, x30, [var_10h]
|           0x100003f98      ff830091       add sp, sp, 0x20
\           0x100003f9c      c0035fd6       ret
EOF
RUN

NAME=struct field type propagation from callee prototype
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi+8]
wa mov rdi, rax @ 4
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=struct field type propagation through nested struct pointers
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi+8]
wa mov rax, [rax+0x10] @ 4
wa mov rdi, rax @ 8
wa call 0x20 @ 0xb
wa ret @ 0x10
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Inner { char pad2[16]; int field_16; };"
"td struct Outer { char pad[8]; struct Inner *inner; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Outer *" @ 0
aei
aeim
aaft
aaft
tsc Outer
tsc Inner
EOF
EXPECT=<<EOF
struct Outer {
  char pad[8];
  struct Inner *inner;
};
struct Inner {
  char pad2[16];
  char *field_16;
};
EOF
RUN

NAME=struct field type propagation through callee-saved pointer copy
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, rdi
wa mov rdi, [rbx+8] @ 3
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=struct field type propagation through typedef pointer
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi+8]
wa mov rdi, rax @ 4
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
"td typedef struct Ctx CtxAlias;"
afs int helper(char *s) @ 0x20
afvt arg1 "CtxAlias *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=struct field type propagation folds lea displacement into the deref
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa lea rax, [rdi+8]
wa mov rdi, [rax+0x10] @ 4
wa call 0x20 @ 8
wa ret @ 0xd
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Big { char pad[16]; int a; int a2; int b; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Big *" @ 0
aei
aeim
aaft
tsc Big
EOF
EXPECT=<<EOF
struct Big {
  char pad[16];
  int a;
  int a2;
  char *b;
};
EOF
RUN

NAME=struct field type propagation folds lea displacement across nested structs
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa lea rax, [rdi+8]
wa mov rax, [rax+0x10] @ 4
wa mov rdi, [rax+4] @ 8
wa call 0x20 @ 0xc
wa ret @ 0x11
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Inner { char c[4]; int y; };"
"td struct Outer { char pad[16]; char pad2[8]; struct Inner *inner; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Outer *" @ 0
aei
aeim
aaft
tsc Outer
tsc Inner
EOF
EXPECT=<<EOF
struct Outer {
  char pad[16];
  char pad2[8];
  struct Inner *inner;
};
struct Inner {
  char c[4];
  char *y;
};
EOF
RUN

NAME=struct field type propagation through a spilled pointer reload
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov [rsp+0x10], rdi
wa mov rax, [rsp+0x10] @ 5
wa mov rax, [rax+8] @ 0xa
wa mov rdi, rax @ 0xe
wa call 0x20 @ 0x11
wa ret @ 0x16
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(char *s) @ 0x20
afvt arg_10h "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=no struct field propagation when the base pointer is modified arithmetically
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa add rax, rdi
wa mov rdi, [rax+0x10] @ 3
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[16]; int a; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[16];
  int a;
};
EOF
RUN

NAME=array struct member not retyped by field propagation
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi]
wa mov rdi, rax @ 3
wa call 0x20 @ 6
wa ret @ 0xb
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  int field_8;
};
EOF
RUN

NAME=no struct field propagation through stack pointer reloads
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov [rsp+0x10], rdi
wa mov rax, [rsp+0x10] @ 5
wa mov rdi, rax @ 0xa
wa call 0x20 @ 0xd
wa ret @ 0x12
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[16]; int a; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
afv-arg_10h @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[16];
  int a;
};
EOF
RUN

NAME=struct field type not downgraded by weaker callee type
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi+8]
wa mov rdi, rax @ 4
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; char *field_8; };"
afs int helper(void *p) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=struct field void pointer upgraded to char pointer
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi+8]
wa mov rdi, rax @ 4
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; void *field_8; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=struct field retype rejected when it would overlap next member
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi+8]
wa mov rdi, rax @ 4
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Pair { char pad[8]; int a; int b; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Pair *" @ 0
aei
aeim
aaft
tsc Pair
EOF
EXPECT=<<EOF
struct Pair {
  char pad[8];
  int a;
  int b;
};
EOF
RUN

NAME=struct field typed from callee return value stored into it
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, rdi
wa call 0x20 @ 3
wa mov [rbx+8], rax @ 8
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
"td char *helper();"
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=struct field typed from callee return value stored on arm64
FILE=malloc://64
ARGS=-a arm -b 64 -e types.fields=true
CMDS=<<EOF
wa mov x2, x0
wa bl 0x20 @ 4
wa str x0, [x2, 0x10] @ 8
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[16]; int field_16; };"
"td char *helper();"
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[16];
  char *field_16;
};
EOF
RUN

NAME=struct field typed from return value stored through a nested pointer
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, [rdi+8]
wa call 0x20 @ 4
wa mov [rbx+0x10], rax @ 9
wa ret @ 0xd
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Inner { char pad2[16]; int field_16; };"
"td struct Outer { char pad[8]; struct Inner *inner; };"
"td char *helper();"
afvt arg1 "struct Outer *" @ 0
aei
aeim
aaft
tsc Outer
tsc Inner
EOF
EXPECT=<<EOF
struct Outer {
  char pad[8];
  struct Inner *inner;
};
struct Inner {
  char pad2[16];
  char *field_16;
};
EOF
RUN

NAME=struct field typed from return value through a copied deref base
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, [rdi+8]
wa mov rcx, rbx @ 4
wa call 0x20 @ 7
wa mov [rcx+0x10], rax @ 0xc
wa ret @ 0x10
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Inner { char pad2[16]; int field_16; };"
"td struct Outer { char pad[8]; struct Inner *inner; };"
"td char *helper();"
afvt arg1 "struct Outer *" @ 0
aei
aeim
aaft
tsc Inner
EOF
EXPECT=<<EOF
struct Inner {
  char pad2[16];
  char *field_16;
};
EOF
RUN

NAME=struct field not retyped when the store base was modified by arithmetic
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, [rdi+8]
wa add rbx, 8 @ 4
wa call 0x20 @ 8
wa mov [rbx+0x10], rax @ 0xd
wa ret @ 0x11
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Inner { char pad2[16]; int field_16; };"
"td struct Outer { char pad[8]; struct Inner *inner; };"
"td char *helper();"
afvt arg1 "struct Outer *" @ 0
aei
aeim
aaft
tsc Inner
EOF
EXPECT=<<EOF
struct Inner {
  char pad2[16];
  int field_16;
};
EOF
RUN

NAME=struct field typed from return value stored through a nested pointer on arm64
FILE=malloc://64
ARGS=-a arm -b 64 -e types.fields=true
CMDS=<<EOF
wa ldr x2, [x0, 8]
wa bl 0x20 @ 4
wa str x0, [x2, 0x10] @ 8
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Inner { char pad2[16]; int field_16; };"
"td struct Outer { char pad[8]; struct Inner *inner; };"
"td char *helper();"
afvt arg1 "struct Outer *" @ 0
aei
aeim
aaft
tsc Outer
tsc Inner
EOF
EXPECT=<<EOF
struct Outer {
  char pad[8];
  struct Inner *inner;
};
struct Inner {
  char pad2[16];
  char *field_16;
};
EOF
RUN

NAME=struct field typed from return value through a copied deref base on arm64
FILE=malloc://64
ARGS=-a arm -b 64 -e types.fields=true
CMDS=<<EOF
wa ldr x2, [x0, 8]
wa mov x3, x2 @ 4
wa bl 0x20 @ 8
wa str x0, [x3, 0x10] @ 0xc
wa ret @ 0x10
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Inner { char pad2[16]; int field_16; };"
"td struct Outer { char pad[8]; struct Inner *inner; };"
"td char *helper();"
afvt arg1 "struct Outer *" @ 0
aei
aeim
aaft
tsc Inner
EOF
EXPECT=<<EOF
struct Inner {
  char pad2[16];
  char *field_16;
};
EOF
RUN

NAME=struct field not retyped when the arm64 store base was modified by arithmetic
FILE=malloc://64
ARGS=-a arm -b 64 -e types.fields=true
CMDS=<<EOF
wa ldr x2, [x0, 8]
wa mov x4, x2 @ 4
wa add x2, x2, 8 @ 8
wa bl 0x20 @ 0xc
wa str x0, [x2, 0x10] @ 0x10
wa str x0, [x4, 0x14] @ 0x14
wa ret @ 0x18
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Inner { char pad2[16]; int field_16; int field_20; };"
"td struct Outer { char pad[8]; struct Inner *inner; };"
"td char *helper();"
afvt arg1 "struct Outer *" @ 0
aei
aeim
aaft
tsc Inner
EOF
EXPECT=<<EOF
struct Inner {
  char pad2[16];
  int field_16;
  char *field_20;
};
EOF
RUN

NAME=struct field typed from address passed as out parameter
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa lea rax, [rdi+8]
wa mov rdi, rax @ 4
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Thing { int x; };"
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(struct Thing **out) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  struct Thing *field_8;
};
EOF
RUN

NAME=struct field keeps const from prototype when never written
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi+8]
wa mov rdi, rax @ 4
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(const char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  const char *field_8;
};
EOF
RUN

NAME=struct field const dropped when a write to it is observed
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov [rdi+8], rcx
wa mov rax, [rdi+8] @ 4
wa mov rdi, rax @ 8
wa call 0x20 @ 0xb
wa ret @ 0x10
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(const char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=struct field const dropped when a return value is stored into it
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, rdi
wa call 0x20 @ 3
wa mov [rbx+8], rax @ 8
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; const char *field_8; };"
"td char *helper();"
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=const array member keeps its qualifier when the write follows the call
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, rdi
wa mov rax, [rdi+8] @ 3
wa mov rdi, rax @ 7
wa call 0x20 @ 0xa
wa mov [rbx+8], rcx @ 0xf
wa ret @ 0x13
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; const char field_8[4]; };"
afs int helper(const char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  const char field_8[4];
};
EOF
RUN

NAME=struct field const dropped by an overlapping partial write
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, rdi
wa mov rax, [rdi+8] @ 3
wa mov rdi, rax @ 7
wa call 0x20 @ 0xa
wa mov dword [rbx+12], ecx @ 0xf
wa ret @ 0x13
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(const char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=struct field type propagation on aarch64
FILE=malloc://64
ARGS=-a arm -b 64 -e types.fields=true
CMDS=<<EOF
wa ldr x1, [x0, 8]
wa mov x0, x1 @ 4
wa bl 0x20 @ 8
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=union member typed from callee prototype when unambiguous
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi]
wa mov rdi, rax @ 3
wa call 0x20 @ 6
wa ret @ 0xb
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td union Cell { long num; };"
afs int helper(char *s) @ 0x20
afvt arg1 "union Cell *" @ 0
aei
aeim
aaft
tuc Cell
EOF
EXPECT=<<EOF
union Cell {
  char *num;
};
EOF
RUN

NAME=union member not retyped when the access is ambiguous
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi]
wa mov rdi, rax @ 3
wa call 0x20 @ 6
wa ret @ 0xb
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td union Cell2 { long num; void *ptr; };"
afs int helper(char *s) @ 0x20
afvt arg1 "union Cell2 *" @ 0
aei
aeim
aaft
tuc Cell2
EOF
EXPECT=<<EOF
union Cell2 {
  long num;
  void *ptr;
};
EOF
RUN

NAME=no struct field propagation from stack slot loads
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov [rsp+8], rdi
wa mov rdi, [rsp+8] @ 5
wa call 0x20 @ 0xa
wa ret @ 0xf
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(char *s) @ 0x20
afvt arg_8h "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  int field_8;
};
EOF
RUN

NAME=struct field propagation keeps zero-displacement derefs in the chain
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, rdi
wa mov rax, [rbx] @ 3
wa mov rdi, [rax+8] @ 6
wa call 0x20 @ 0xa
wa ret @ 0xf
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Inner { char pad2[8]; int fld; };"
"td struct Outer { struct Inner *inner; int keep; int at8; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Outer *" @ 0
aei
aeim
aaft
tsc Outer
tsc Inner
EOF
EXPECT=<<EOF
struct Outer {
  struct Inner *inner;
  int keep;
  int at8;
};
struct Inner {
  char pad2[8];
  char *fld;
};
EOF
RUN

NAME=union array member survives a sibling retype
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi]
wa mov rdi, rax @ 3
wa call 0x20 @ 6
wa ret @ 0xb
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td union Cell3 { long num; char buf[16]; };"
afs int helper(char *s) @ 0x20
afvt arg1 "union Cell3 *" @ 0
aei
aeim
aaft
tuc Cell3
EOF
EXPECT=<<EOF
union Cell3 {
  char *num;
  char buf[16];
};
EOF
RUN

NAME=no struct field propagation when types.fields is disabled
FILE=malloc://64
ARGS=-a x86 -b 64
CMDS=<<EOF
wa mov rax, [rdi+8]
wa mov rdi, rax @ 4
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  int field_8;
};
EOF
RUN

NAME=struct field not retyped when the deref hop offset matches no member
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rdi, [rdi+0x18]
wa call 0x20 @ 4
wa mov [rdi+8], rax @ 9
wa ret @ 0xd
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
"td char *helper();"
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  int field_8;
};
EOF
RUN

NAME=char pointer member survives a scalar pointer prototype
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi+8]
wa mov rdi, rax @ 4
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; char *field_8; };"
afs int helper(int *p) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=struct field const dropped when the write follows the call
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, rdi
wa mov rax, [rdi+8] @ 3
wa mov rdi, rax @ 7
wa call 0x20 @ 0xa
wa mov [rbx+8], rcx @ 0xf
wa ret @ 0x13
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(const char *s) @ 0x20
afvt arg1 "struct Ctx *" @ 0
aei
aeim
aaft
tsc Ctx
EOF
EXPECT=<<EOF
struct Ctx {
  char pad[8];
  char *field_8;
};
EOF
RUN

NAME=dead store to the argument register does not divert field typing
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, rsi
wa mov rdi, [rdi+0x20] @ 3
wa mov rdi, [rax+8] @ 7
wa call 0x20 @ 0xb
wa ret @ 0x10
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct C { char p[8]; int m8; };"
"td struct A { char p[32]; struct C *e; };"
"td struct B { char p[8]; long y; };"
afs int helper(char *s) @ 0x20
afvt arg1 "struct A *" @ 0
afvt arg2 "struct B *" @ 0
aei
aeim
aaft
tsc C
tsc B
EOF
EXPECT=<<EOF
struct C {
  char p[8];
  int m8;
};
struct B {
  char p[8];
  char *y;
};
EOF
RUN

NAME=base pointer vars keep their types during field propagation
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov [rsp+0x10], rdi
wa mov rax, [rsp+0x10] @ 5
wa mov rax, [rax+8] @ 0xa
wa mov rdi, rax @ 0xe
wa call 0x20 @ 0x11
wa ret @ 0x16
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Ctx { char pad[8]; int field_8; };"
afs int helper(char *s) @ 0x20
aei
aeim
aaft
afv @ 0
EOF
EXPECT=<<EOF
arg int64_t arg1 @ rdi
arg int64_t arg_10h @ rsp+0x10
EOF
RUN

NAME=struct field typed from return value stored at offset zero
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, rdi
wa call 0x20 @ 3
wa mov [rbx], rax @ 8
wa ret @ 0xb
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Zed { long first; };"
"td char *helper();"
afvt arg1 "struct Zed *" @ 0
aei
aeim
aaft
tsc Zed
EOF
EXPECT=<<EOF
struct Zed {
  char *first;
};
EOF
RUN

NAME=struct field typed from return value stored at a large displacement
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rbx, rdi
wa call 0x20 @ 3
wa mov [rbx+0x800], rax @ 8
wa ret @ 0xf
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td struct Far { char pad[2048]; long deep; };"
"td char *helper();"
afvt arg1 "struct Far *" @ 0
aei
aeim
aaft
tsc Far
EOF
EXPECT=<<EOF
struct Far {
  char pad[2048];
  char *deep;
};
EOF
RUN

NAME=hex array dimension survives a sibling member retype
FILE=malloc://64
ARGS=-a x86 -b 64 -e types.fields=true
CMDS=<<EOF
wa mov rax, [rdi+8]
wa mov rdi, rax @ 4
wa call 0x20 @ 7
wa ret @ 0xc
wa ret @ 0x20
af @ 0x20
afn helper @ 0x20
af @ 0
afn caller @ 0
"td typedef int handle_t;"
"td struct Hz { char pad[8]; int x; char tail[0x10]; };"
afs int helper(handle_t h) @ 0x20
afvt arg1 "struct Hz *" @ 0
aei
aeim
aaft
tk struct.Hz.x
tk struct.Hz.tail
EOF
EXPECT=<<EOF
handle_t,8,0
char,12,16
EOF
RUN

NAME=struct typedef gets a toplevel kind key
FILE=malloc://64
ARGS=-a x86 -b 64
CMDS=<<EOF
"td struct Ctx { int x; };"
"td typedef struct Ctx CtxAlias;"
tk CtxAlias
tk typedef.CtxAlias
EOF
EXPECT=<<EOF
typedef
struct Ctx
EOF
RUN
