/* Parameter classification probe for the Programming Language Atlas.
 *
 * One source, two ABIs. Every claim on calling.html about where a parameter
 * lives comes from compiling this file for both targets and reading the first
 * instruction of each callee: an operand naming a register means the parameter
 * arrived in that register, an operand dereferencing one means it arrived by
 * address.
 *
 *   clang -O2 -S -masm=intel --target=x86_64-pc-linux-gnu    -o sysv.s classify.c
 *   clang -O2 -S -masm=intel --target=x86_64-pc-windows-msvc -o win.s  classify.c
 *
 * Freestanding on purpose: no headers, so the Windows target needs no SDK.
 */

/* --- Aggregates chosen to straddle both ABIs' decision boundaries. --- */
typedef struct { int a, b; }           Small8;    /*  8 bytes            */
typedef struct { int a, b; double c; } Packet16;  /* 16, INTEGER + SSE   */
typedef struct { double x, y; }        Vec2;      /* 16, SSE + SSE       */
typedef struct { double x, y, z; }     Vec3;      /* 24, over two eightbytes */
typedef struct { int a, b, c; }        Odd12;     /* 12, not a power of two  */

int    take_small8(Small8   s) { return s.a + s.b; }
double take_p16   (Packet16 p) { return p.a + p.b + p.c; }
double take_vec2  (Vec2     v) { return v.x + v.y; }
double take_vec3  (Vec3     v) { return v.x + v.y + v.z; }
int    take_odd12 (Odd12    o) { return o.a + o.b + o.c; }

/* Returns classify by the same rules, so a large return value silently adds a
   parameter the source never declared. */
Small8 make_small8(int a, int b) { Small8 s = {a, b};          return s; }
Vec2   make_vec2  (double x)     { Vec2   v = {x, x + 1};      return v; }
Vec3   make_vec3  (double x)     { Vec3   v = {x, x+1, x+2};   return v; }

/* --- The size sweep behind the "power of two, at most eight" predicate. --- */
typedef struct { char a; }           S1;
typedef struct { short a; }          S2;
typedef struct { char a, b, c; }     S3;   /* 3  */
typedef struct { int a; }            S4;
typedef struct { int a; char b; }    S5;   /* 8, padded  */
typedef struct { double a; }         S8;
typedef struct { double a; char b; } S9;   /* 16, padded */
typedef struct { double a, b; }      S16;

int    f1 (S1  s) { return s.a; }
int    f2 (S2  s) { return s.a; }
int    f3 (S3  s) { return s.a + s.c; }
int    f4 (S4  s) { return s.a; }
int    f5 (S5  s) { return s.a + s.b; }
double f8 (S8  s) { return s.a; }
double f9 (S9  s) { return s.a + s.b; }
double f16(S16 s) { return s.a + s.b; }

/* --- Where each ABI runs out of registers. Six scalars, alternating class. --- */
double six(double a, int b, double c, int d, double e, int f)
{ return a + b + c + d + e + f; }

/* --- Caller side: who materialises the copy, and who can still tail-call. --- */
double mk_call_vec2(double a) { Vec2 v = { a, a + 1 };    return take_vec2(v); }
double mk_call_vec3(double a) { Vec3 v = { a, a+1, a+2 }; return take_vec3(v); }
double call_six(double a)     { return six(a, 1, a, 2, a, 3); }
