| /* This tests returning of structures. */ |
| |
| #include "defines.h" |
| #include "macros.h" |
| #include "args.h" |
| |
| struct IntegerRegisters iregbits = { ~0, ~0, ~0, ~0, ~0, ~0 }; |
| struct IntegerRegisters iregs; |
| unsigned int num_iregs; |
| |
| int current_test; |
| int num_failed = 0; |
| |
| typedef enum { |
| EAX = 0, |
| EAX_EDX, |
| LONG_LONG, |
| FLOAT, |
| DOUBLE, |
| FLOAT_FLOAT, |
| EAX_FLOAT, |
| FLOAT_EDX, |
| MEM |
| } Type; |
| |
| /* Structures which should be returned in EAX/LONG_LONG/EAX_EDX. */ |
| #define D(I,MEMBERS,C,B) struct S_ ## I { MEMBERS ; }; Type class_ ## I = C; \ |
| struct S_ ## I f_ ## I (void) { struct S_ ## I s; iamcu_memset (&s, 0, sizeof(s)); B; return s; } |
| |
| D(1,char m1, EAX, s.m1=42) |
| D(2,short m1, EAX, s.m1=42) |
| D(3,int m1, EAX, s.m1=42) |
| D(4,char m1[3], EAX, s.m1[0]=42) |
| D(5,char m1[4], EAX, s.m1[0]=42) |
| D(6,char m1;char m2; char m3, EAX, s.m1=42) |
| D(7,char m1;short m2, EAX, s.m1=42) |
| |
| D(30,long long m1, LONG_LONG, s.m1=0xadadbeefadadbeefLL) |
| |
| D(50,short m1;int m2, EAX_EDX, s.m1=42; s.m2=43) |
| D(51,char m1;int m2, EAX_EDX, s.m1=42; s.m2=43) |
| D(52,char m1[5], EAX_EDX, s.m1[0]=42; s.m1[4]=43) |
| D(53,char m1[6], EAX_EDX, s.m1[0]=42; s.m1[4]=43) |
| D(54,char m1[7], EAX_EDX, s.m1[0]=42; s.m1[4]=43) |
| D(55,char m1[8], EAX_EDX, s.m1[0]=42; s.m1[4]=43) |
| D(56,char m1;short m2[2], EAX_EDX, s.m1=42; s.m2[1]=43) |
| D(57,short m1[4], EAX_EDX, s.m1[0]=42; s.m1[2]=43) |
| D(58,int m1[2], EAX_EDX, s.m1[0]=42; s.m1[1]=43) |
| D(59,int m1;char m2, EAX_EDX, s.m1=42; s.m2=43) |
| D(60,int m1;short m2, EAX_EDX, s.m1=42; s.m2=43) |
| D(61,int m1;short m2; char m3, EAX_EDX, s.m1=42; s.m2=43) |
| D(62,int m1;char m2; short m3, EAX_EDX, s.m1=42; s.m2=43) |
| |
| /* Packed members. */ |
| D(100,short m1[1];int m2 PACKED, EAX_EDX, s.m1[0]=42; s.m2=43) |
| D(101,char m1; short m2 PACKED; char m3, EAX_EDX, s.m1=42; s.m3=43) |
| |
| /* Structures which should be returned in FLOAT/DOUBLE. */ |
| #undef D |
| #define D(I,MEMBERS,C,B) struct S_ ## I { MEMBERS ; }; Type class_ ## I = C; \ |
| struct S_ ## I f_ ## I (void) { struct S_ ## I s; iamcu_memset (&s, 0, sizeof(s)); B; return s; } |
| |
| D(200,float f, FLOAT, s.f=42) |
| D(201,double d, DOUBLE, s.d=42) |
| |
| D(300,float m;char m2, FLOAT_EDX, s.m=42; s.m2=43) |
| D(301,float m;short m2, FLOAT_EDX, s.m=42; s.m2=43) |
| D(302,float m;int m2, FLOAT_EDX, s.m=42; s.m2=43) |
| |
| D(400,char m1; float m2, EAX_FLOAT, s.m1=42; s.m2=43) |
| D(401,short m1; float m2, EAX_FLOAT, s.m1=42; s.m2=43) |
| D(402,int m1; float m2, EAX_FLOAT, s.m1=42; s.m2=43) |
| |
| D(500,float m;float m2, FLOAT_FLOAT, s.m=42; s.m2=43) |
| D(501,float f[2], FLOAT, s.f[0]=42; s.f[1]=43) |
| |
| /* Structures which should be returned in MEM. */ |
| void *struct_addr; |
| #undef D |
| #define D(I,MEMBERS) struct S_ ## I { MEMBERS ; }; Type class_ ## I = MEM; \ |
| struct S_ ## I f_ ## I (void) { union {unsigned char c; struct S_ ## I s;} u; iamcu_memset (&u.s, 0, sizeof(u.s)); u.c = 42; return u.s; } |
| |
| /* Too large. */ |
| D(600,char m1[17]) |
| D(601,short m1[9]) |
| D(602,int m1[5]) |
| D(603,long m1[3]) |
| D(604,short m1[8];char c) |
| D(605,char m1[1];int i[4]) |
| D(606,float m1[5]) |
| D(607,double m1[3]) |
| D(608,char m1[1];float f[4]) |
| D(609,char m1[1];double d[2]) |
| D(610,__complex long double m1[1]) |
| |
| /* Too large due to padding. */ |
| D(611,char m1[1]; int i; char c2) |
| |
| /* Special tests. */ |
| #undef D |
| #define D(I,MEMBERS,B) struct S_ ## I { MEMBERS ; }; Type class_ ## I = MEM; \ |
| struct S_ ## I f_ ## I (void) { struct S_ ## I s; B; return s; } |
| D(700,float f[4], s.f[0] = s.f[1] = s.f[2] = s.f[3] = 42) |
| |
| void |
| check_eax (void) |
| { |
| switch (current_test) |
| { |
| case 1: |
| case 4: |
| case 5: |
| case 6: |
| case 7: |
| eax &= 0xff; |
| break; |
| case 2: |
| eax &= 0xffff; |
| break; |
| case 3: |
| eax &= 0xffff; |
| break; |
| default: |
| abort (); |
| } |
| if (eax != 42) |
| num_failed++; |
| } |
| |
| void |
| check_eax_edx (void) |
| { |
| unsigned long long ll = eax | ((unsigned long long) edx) << 32; |
| switch (current_test) |
| { |
| case 50: |
| eax &= 0xffff; |
| break; |
| case 52: |
| case 53: |
| case 54: |
| case 55: |
| edx &= 0xff; |
| case 51: |
| eax &= 0xff; |
| break; |
| case 56: |
| eax &= 0xff; |
| edx &= 0xffff; |
| break; |
| case 57: |
| eax &= 0xffff; |
| edx &= 0xffff; |
| break; |
| case 58: |
| break; |
| case 59: |
| case 62: |
| edx &= 0xff; |
| break; |
| case 60: |
| case 61: |
| edx &= 0xffff; |
| break; |
| case 100: |
| eax &= 0xffff; |
| edx = (ll >> 16) & 0xffffffff; |
| break; |
| case 101: |
| edx = (eax >> 24) & 0xff; |
| eax &= 0xff; |
| break; |
| default: |
| abort (); |
| } |
| if (eax != 42 || edx != 43) |
| num_failed++; |
| } |
| |
| void |
| check_float_edx (void) |
| { |
| union |
| { |
| unsigned long l; |
| float f; |
| } ueax; |
| switch (current_test) |
| { |
| case 300: |
| edx &= 0xff; |
| break; |
| case 301: |
| edx &= 0xffff; |
| break; |
| case 302: |
| edx &= 0xffff; |
| break; |
| default: |
| abort (); |
| } |
| ueax.l = eax; |
| if (ueax.f != 42 || edx != 43) |
| num_failed++; |
| } |
| |
| void |
| check_eax_float (void) |
| { |
| union |
| { |
| unsigned long l; |
| float f; |
| } uedx; |
| switch (current_test) |
| { |
| case 400: |
| eax &= 0xff; |
| break; |
| case 401: |
| eax &= 0xffff; |
| break; |
| case 402: |
| eax &= 0xffff; |
| break; |
| default: |
| abort (); |
| } |
| uedx.l = edx; |
| if (eax != 42 || uedx.f != 43) |
| num_failed++; |
| } |
| |
| void |
| check_float_float (void) |
| { |
| union |
| { |
| unsigned long l; |
| float f; |
| } ueax, uedx; |
| switch (current_test) |
| { |
| case 500: |
| case 501: |
| break; |
| default: |
| abort (); |
| } |
| ueax.l = eax; |
| uedx.l = edx; |
| if (ueax.f != 42 || uedx.f != 43) |
| num_failed++; |
| } |
| |
| void |
| check_all (Type class, unsigned long size) |
| { |
| union |
| { |
| struct |
| { |
| unsigned long eax; |
| unsigned long edx; |
| } eax_edx; |
| unsigned long long ll; |
| float f; |
| double d; |
| } u; |
| |
| switch (class) |
| { |
| case EAX: |
| check_eax (); |
| break; |
| case LONG_LONG: |
| if (0xadadbeefL != eax || 0xadadbeefL != edx) |
| num_failed++; |
| break; |
| case EAX_EDX: |
| check_eax_edx (); |
| break; |
| case FLOAT: |
| u.eax_edx.eax = eax; |
| if (u.f != 42) |
| num_failed++; |
| break; |
| case DOUBLE: |
| u.eax_edx.eax = eax; |
| u.eax_edx.edx = edx; |
| if (u.d != 42) |
| num_failed++; |
| break; |
| case FLOAT_EDX: |
| check_float_edx (); |
| break; |
| case FLOAT_FLOAT: |
| check_float_float (); |
| break; |
| case EAX_FLOAT: |
| check_eax_float (); |
| break; |
| case MEM: |
| /* sret_eax contains a slot whose address is given to the f_* |
| functions. The slot may be a temporary one on stack. When |
| this function is called, hopefully this slot hasn't be |
| overriden. */ |
| if (sret_eax != eax) |
| num_failed++; |
| else if (current_test < 700) |
| { |
| if (*(unsigned char*)sret_eax != 42 |
| || *(unsigned char*)struct_addr != 42) |
| num_failed++; |
| } |
| else |
| { |
| if (*(float *)sret_eax != 42 |
| || *(float *)struct_addr != 42) |
| num_failed++; |
| } |
| break; |
| } |
| } |
| |
| #undef D |
| #define D(I) { static struct S_ ## I s; current_test = I; struct_addr = (void*)&s; \ |
| clear_non_sret_int_registers; \ |
| s = WRAP_RET(f_ ## I) (); \ |
| check_all(class_ ## I, sizeof(s)); \ |
| } |
| |
| int |
| main (void) |
| { |
| D(1) D(2) D(3) D(4) D(5) D(6) D(7) |
| |
| D(30) |
| |
| D(50) D(51) D(52) D(53) D(54) D(55) D(56) D(57) D(58) D(59) |
| D(60) D(61) D(62) |
| |
| D(100) D(101) |
| |
| D(200) D(201) |
| |
| D(300) D(301) D(302) |
| |
| D(400) D(401) D(402) |
| |
| D(500) D(501) |
| |
| D(600) D(601) D(602) D(603) D(604) D(605) D(606) D(607) D(608) D(609) |
| D(610) D(611) |
| |
| D(700) |
| |
| if (num_failed) |
| abort (); |
| |
| return 0; |
| } |