NAME it mutates the passed variable
PROG macro inc($x) { $x += 1; } begin { $a = 1; inc($a); print($a);  }
EXPECT 2

NAME it assigns to the last expression
PROG macro inc($x) { $x += 1; $x } begin { $a = 1; $b = inc($a); print(($a, $b));  }
EXPECT (2, 2)

NAME it mutates the passed non-scalar map
PROG macro set(@x) { @x[1] = 2; } begin { @a[1] = 1; set(@a);  }
EXPECT @a[1]: 2

NAME it mutates the passed scalar map
PROG macro set(@x) { @x = 2; } begin { @a = 1; set(@a);  }
EXPECT @a: 2

NAME it renames internal variables
PROG macro add(x) { let $y; if (true) { $y = 1 } x + $y  } begin { print(add(1));  }
EXPECT 2

NAME it reads the passed map
PROG macro get(@x) { 1 + @x[1] } begin { @a[1] = 1; print(get(@a));  }
EXPECT 2

NAME it can have side effects
PROG macro print_me($x) { print(("me", $x)); } begin { $a = 1; print_me($a);  }
EXPECT (me, 1)

NAME it can exit early
PROG macro early() { exit(); } begin { early(); print(1); }
EXPECT_NONE 1

NAME it can call other macros as expressions
PROG macro add1(x) { x + 1 } macro add2(y) { y + add1(y) } macro add3(z) { z + add2(z) } begin { print(add3(1));  }
EXPECT 4

NAME it can call other macros as statements
PROG macro add1($x) { $x += 1; } macro add2($y) { add1($y); $y += 1; } macro add3($z) { add2($z); $z += 1; } begin { $a = 1; add3($a); print($a);  }
EXPECT 4

NAME builtin wrapper definition order does not matter
PROG macro add2(x) { x + add1(x) } macro add3(x) { x + add2(x) } macro add1(x) { x + 1 } begin { print(add3(1));  }
EXPECT 4

NAME it accepts arbitrary expressions without variables or maps
PROG macro add_one(x) { x + 1 } begin { $a = 1; print(add_one(1 + 1));  }
EXPECT 3

NAME it accepts arbitrary expressions with variables
PROG macro add_one(x) { x + 1 } begin { $a = 1; print(add_one($a + 1));  }
EXPECT 3

NAME it accepts arbitrary expressions with variables nested
PROG macro add_two(x) { x + 2 } macro add_one(x) { add_two(x + 1) + 1 } begin { $a = 1; print(add_one($a + 1));  }
EXPECT 6

NAME it accepts arbitrary expressions with maps
PROG macro add_one(x) { x + 1 } begin { @a = 1; print(add_one(@a + 1));  }
EXPECT 3

NAME can call the same macro multiple times
PROG macro inc($x) { $x += 1; } begin { $a = 1; inc($a); inc($a); inc($a); print($a);  }
EXPECT 4

NAME for loops can be in macros
PROG macro loop_map(@a) { let $x = 1; for ($kv : @a) { $x += $kv.1;} $x } begin { @x[1] = 5; @x[2] = 10; print(loop_map(@x));  }
EXPECT 16

NAME it works in nested scopes
PROG macro add1($x) { $x + 1 } macro add2($x) { $x + 2 } begin { $a = 1; if ($a == 1) { print(add2($a)); } else { print(add1($a)); }  }
EXPECT 3

NAME it re-names variables to prevent collision
PROG macro inc(x) { $y = x + 1; $y } begin { $y = 2; $z = inc(5); print(($y, $z));  }
EXPECT (2, 6)

NAME it re-names decl variables to prevent collision
PROG macro inc(x) { let $y = x + 1; $y } begin { $y = 2; $z = inc(5); print(($y, $z));  }
EXPECT (2, 6)

NAME it can be part of an expression passed to a macro
PROG macro add_one(x) { x + 1 } begin { $a = add_one(add_one(1) + 1); print($a);  }
EXPECT 4

NAME it expands idents if there is a matching macro
PROG macro one() { $x = 1; $x } begin { $a = one; print($a);  }
EXPECT 1

NAME it replaces the passed in expression in multiple expression locations
PROG macro inc(x) { $y = x + 1; $z = x + 2; $y + $z } begin { $y = 2; print(inc({ $y +=1; $y })); }
EXPECT 10

NAME it replaces the passed in expression in multiple expression statement locations
PROG macro side_effects(x) { x; x; x; } begin { side_effects({ printf("hi") }); }
EXPECT hihihi

NAME the same macro can be called multiple times in a passed expression
PROG macro add_one(x) { x + 1 } begin { print(add_one(add_one(add_one(1)))); }
EXPECT 4

NAME macro arg idents take precedence over macros with no arguments
PROG macro add() { 2 } macro ident(add) { add } begin { print(ident(1)); }
EXPECT 1

# This is more of a regression test for clone that wasn't cloning the Iterable node
NAME macro replaces all instances
PROG macro first_key(@x) { let $x; for ($k : @x) { $x = $k.0; break; } $x } begin { @a[1] = 0; @b[2] = 0; print((first_key(@a), first_key(@b))); }
EXPECT (1, 2)

# Test builtins
NAME builtin wrapper comm
PROG begin { if (__builtin_comm == comm() && __builtin_comm == comm) { print(comm); } }
EXPECT bpftrace

NAME builtin wrapper cgroup
PROG begin { if (__builtin_cgroup == cgroup() && __builtin_cgroup == cgroup) { printf("SUCCESS %llu\n", cgroup); } }
EXPECT_REGEX SUCCESS [0-9]+

NAME builtin wrapper cpid
RUN {{BPFTRACE}} -e 'begin { if (__builtin_cpid == cpid() && __builtin_cpid == cpid) { printf("SUCCESS %llu\n", cpid); } }' -c './testprogs/syscall nanosleep 1e9'
EXPECT_REGEX SUCCESS [0-9]+
TIMEOUT 3

NAME builtin wrapper cpu
PROG begin { if (__builtin_cpu == cpu() && __builtin_cpu == cpu) { printf("SUCCESS %llu\n", cpu); } }
EXPECT_REGEX SUCCESS [0-9]+

NAME builtin wrapper curtask
PROG begin { if (__builtin_curtask == curtask() && __builtin_curtask == curtask) { printf("SUCCESS %llu\n", curtask); } }
EXPECT_REGEX SUCCESS [0-9]+

NAME builtin wrapper elapsed
PROG begin { printf("SUCCESS1 %llu\n", __builtin_elapsed); printf("SUCCESS2 %llu\n", elapsed); printf("SUCCESS3 %llu\n", elapsed()); }
EXPECT_REGEX SUCCESS1 [0-9]+
EXPECT_REGEX SUCCESS2 [0-9]+
EXPECT_REGEX SUCCESS3 [0-9]+

NAME builtin wrapper func
PROG k:vfs_read { if (__builtin_func == func() && __builtin_func == func) { print(func); } exit(); }
EXPECT vfs_read
AFTER ./testprogs/syscall read

NAME builtin wrapper gid
PROG begin { if (__builtin_gid == gid() && __builtin_gid == gid) { print(gid); } }
EXPECT_REGEX [0-9][0-9]*

NAME builtin wrapper jiffies
PROG begin { if (__builtin_jiffies == jiffies() && __builtin_jiffies == jiffies) { printf("SUCCESS %llu\n", jiffies);} }
EXPECT_REGEX SUCCESS [0-9]+
REQUIRES_FEATURE jiffies64
MIN_KERNEL 5.9

NAME builtin wrapper ncpus
PROG begin { if (__builtin_ncpus == ncpus() && __builtin_ncpus == ncpus) { print(ncpus); } }
EXPECT_REGEX ^[1-9][0-9]*$

NAME builtin wrapper numaid
PROG begin { if (__builtin_numaid == numaid() && __builtin_numaid == numaid) { printf("SUCCESS %d\n", numaid); } }
EXPECT_REGEX SUCCESS [0-9]+

NAME builtin wrapper probe
PROG k:do_nanosleep { if (__builtin_probe == probe() && __builtin_probe == probe) { print(probe); } exit(); }
EXPECT kprobe:do_nanosleep
AFTER ./testprogs/syscall nanosleep 1e8

NAME builtin wrapper rand
PROG begin { printf("SUCCESS1 %llu\n", __builtin_rand); printf("SUCCESS2 %llu\n", rand); printf("SUCCESS3 %llu\n", rand()); }
EXPECT_REGEX SUCCESS1 [0-9]+
EXPECT_REGEX SUCCESS2 [0-9]+
EXPECT_REGEX SUCCESS3 [0-9]+

NAME builtin wrapper retval
PROG kretprobe:vfs_read { if (__builtin_retval == retval() && __builtin_retval == retval) { printf("SUCCESS %d\n", retval); exit(); } }
EXPECT_REGEX SUCCESS .*
AFTER ./testprogs/syscall read

NAME builtin wrapper uid
PROG begin { if (__builtin_uid == uid() && __builtin_uid == uid) { printf("SUCCESS %d\n", uid); } }
EXPECT_REGEX SUCCESS [0-9]+

NAME builtin wrapper usermode
PROG begin { if (__builtin_usermode == usermode() && __builtin_usermode == usermode) { printf("SUCCESS %d\n", usermode); } }
EXPECT_REGEX SUCCESS 1
ARCH x86_64

NAME builtin wrapper username
PROG begin { if (__builtin_username == username() && __builtin_username == username) { printf("SUCCESS %s\n", username); } }
EXPECT_REGEX SUCCESS .*
