| @@ -25,6 +25,24 @@ · 24 unmodified lines |
| 25 | /* number of available registers */ | 25 | /* number of available registers */ |
| 26 | #define NB_REGS 25 | 26 | #define NB_REGS 25 |
| 27 | #define NB_ASM_REGS 8 | 27 | #define NB_ASM_REGS 8 |
| | 28 | #define REG_ARGS_MAX 2 /* at most 2 registers used for each argument */ |
| | 29 | |
| | 30 | #ifdef TCC_TARGET_PE |
| | 31 | typedef int RegArgs; |
| | 32 | #else |
| | 33 | /* This struct stores the struct offsets at which %rax, %rdx, %xmm0, and |
| | 34 | * %xmm1 are to be stored. |
| | 35 | * |
| | 36 | * struct { long long l; double x; }: ireg = { 0, -1 } freg = { 8, -1 } |
| | 37 | * struct { double x; long long l; }: ireg = { 8, -1 } freg = { 0, -1 } |
| | 38 | * struct { long long l; long long l2; }: ireg = { 0, 8 } freg = { -1, -1 } |
| | 39 | * struct { double x; double x2; }: ireg = { -1, -1 } freg = { 0, 8 } |
| | 40 | */ |
| | 41 | typedef struct { |
| | 42 | int ireg[REG_ARGS_MAX]; |
| | 43 | int freg[REG_ARGS_MAX]; |
| | 44 | } RegArgs; |
| | 45 | #endif |
| 28 | | 46 | |
| 29 | /* a register can belong to several classes. The classes must be | 47 | /* a register can belong to several classes. The classes must be |
| 30 | sorted from more general to more precise (see gv2() code which does | 48 | sorted from more general to more precise (see gv2() code which does |
| @@ -745,9 +763,14 @@ void gen_offs_sp(int b, int r, int d) · 714 unmodified lines |
| 745 | } | 763 | } |
| 746 | } | 764 | } |
| 747 | | 765 | |
| | 766 | ST_FUNC int regargs_nregs(RegArgs *args) |
| | 767 | { |
| | 768 | return *args; |
| | 769 | } |
| | 770 | |
| 748 | /* Return the number of registers needed to return the struct, or 0 if | 771 | /* Return the number of registers needed to return the struct, or 0 if |
| 749 | returning via struct pointer. */ | 772 | returning via struct pointer. */ |
| 750 | ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) | 773 | ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args) |
| 751 | { | 774 | { |
| 752 | int size, align; | 775 | int size, align; |
| 753 | *regsize = 8; | 776 | *regsize = 8; |
| @@ -755,20 +778,22 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int · 1 unmodified line |
| 755 | size = type_size(vt, &align); | 778 | size = type_size(vt, &align); |
| 756 | ret->ref = NULL; | 779 | ret->ref = NULL; |
| 757 | if (size > 8) { | 780 | if (size > 8) { |
| 758 | return 0; | 781 | *args = 0; |
| 759 | } else if (size > 4) { | 782 | } else if (size > 4) { |
| 760 | ret->t = VT_LLONG; | 783 | ret->t = VT_LLONG; |
| 761 | return 1; | 784 | *args = 1; |
| 762 | } else if (size > 2) { | 785 | } else if (size > 2) { |
| 763 | ret->t = VT_INT; | 786 | ret->t = VT_INT; |
| 764 | return 1; | 787 | *args = 1; |
| 765 | } else if (size > 1) { | 788 | } else if (size > 1) { |
| 766 | ret->t = VT_SHORT; | 789 | ret->t = VT_SHORT; |
| 767 | return 1; | 790 | *args = 1; |
| 768 | } else { | 791 | } else { |
| 769 | ret->t = VT_BYTE; | 792 | ret->t = VT_BYTE; |
| 770 | return 1; | 793 | *args = 1; |
| 771 | } | 794 | } |
| | 795 | |
| | 796 | return *args != 0; |
| 772 | } | 797 | } |
| 773 | | 798 | |
| 774 | static int is_sse_float(int t) { | 799 | static int is_sse_float(int t) { |
| @@ -1041,7 +1066,9 @@ static X86_64_Mode classify_x86_64_merge(X86_64_Mode a, X86_64_Mode b) · 266 unmodified lines |
| 1041 | return x86_64_mode_sse; | 1066 | return x86_64_mode_sse; |
| 1042 | } | 1067 | } |
| 1043 | | 1068 | |
| 1044 | static X86_64_Mode classify_x86_64_inner(CType *ty) | 1069 | /* classify the x86 eightbytes from byte index start to byte index |
| | 1070 | * end, at offset offset from the root struct */ |
| | 1071 | static X86_64_Mode classify_x86_64_inner(CType *ty, int offset, int start, int end) |
| 1045 | { | 1072 | { |
| 1046 | X86_64_Mode mode; | 1073 | X86_64_Mode mode; |
| 1047 | Sym *f; | 1074 | Sym *f; |
| @@ -1067,8 +1094,10 @@ static X86_64_Mode classify_x86_64_inner(CType *ty) · 19 unmodified lines |
| 1067 | f = ty->ref; | 1094 | f = ty->ref; |
| 1068 | | 1095 | |
| 1069 | mode = x86_64_mode_none; | 1096 | mode = x86_64_mode_none; |
| 1070 | for (f = f->next; f; f = f->next) | 1097 | while ((f = f->next) != NULL) { |
| 1071 | mode = classify_x86_64_merge(mode, classify_x86_64_inner(&f->type)); | 1098 | if (f->c + offset >= start && f->c + offset < end) |
| | 1099 | mode = classify_x86_64_merge(mode, classify_x86_64_inner(&f->type, f->c + offset, start, end)); |
| | 1100 | } |
| 1072 | | 1101 | |
| 1073 | return mode; | 1102 | return mode; |
| 1074 | } | 1103 | } |
| @@ -1076,61 +1105,79 @@ static X86_64_Mode classify_x86_64_inner(CType *ty) · 1 unmodified line |
| 1076 | assert(0); | 1105 | assert(0); |
| 1077 | } | 1106 | } |
| 1078 | | 1107 | |
| 1079 | static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *palign, int *reg_count) | 1108 | static X86_64_Mode classify_x86_64_arg_eightbyte(CType *ty, int offset) |
| 1080 | { | 1109 | { |
| 1081 | X86_64_Mode mode; | 1110 | X86_64_Mode mode; |
| | 1111 | |
| | 1112 | assert((ty->t & VT_BTYPE) == VT_STRUCT); |
| | 1113 | |
| | 1114 | mode = classify_x86_64_inner(ty, 0, offset, offset + 8); |
| | 1115 | |
| | 1116 | return mode; |
| | 1117 | } |
| | 1118 | |
| | 1119 | static void regargs_init(RegArgs *args) |
| | 1120 | { |
| | 1121 | int i; |
| | 1122 | for(i=0; i<REG_ARGS_MAX; i++) { |
| | 1123 | args->ireg[i] = -1; |
| | 1124 | args->freg[i] = -1; |
| | 1125 | } |
| | 1126 | } |
| | 1127 | |
| | 1128 | static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *palign, RegArgs *args) |
| | 1129 | { |
| | 1130 | X86_64_Mode mode = x86_64_mode_none; |
| 1082 | int size, align, ret_t = 0; | 1131 | int size, align, ret_t = 0; |
| 1083 | | 1132 | int ireg = 0, freg = 0; |
| | 1133 | |
| | 1134 | if (args) |
| | 1135 | regargs_init(args); |
| | 1136 | |
| 1084 | if (ty->t & (VT_BITFIELD|VT_ARRAY)) { | 1137 | if (ty->t & (VT_BITFIELD|VT_ARRAY)) { |
| 1085 | *psize = 8; | 1138 | *psize = 8; |
| 1086 | *palign = 8; | 1139 | *palign = 8; |
| 1087 | *reg_count = 1; | 1140 | if (args) |
| | 1141 | args->ireg[ireg++] = 0; |
| 1088 | ret_t = ty->t; | 1142 | ret_t = ty->t; |
| 1089 | mode = x86_64_mode_integer; | 1143 | mode = x86_64_mode_integer; |
| 1090 | } else { | 1144 | } else { |
| 1091 | size = type_size(ty, &align); | 1145 | size = type_size(ty, &align); |
| 1092 | *psize = (size + 7) & ~7; | 1146 | *psize = (size + 7) & ~7; |
| 1093 | *palign = (align + 7) & ~7; | 1147 | *palign = (align + 7) & ~7; |
| 1094 | | 1148 | |
| 1095 | if (size > 16) { | 1149 | if (size > 16) { |
| 1096 | mode = x86_64_mode_memory; | 1150 | mode = x86_64_mode_memory; |
| 1097 | } else { | 1151 | } else { |
| 1098 | mode = classify_x86_64_inner(ty); | 1152 | int start; |
| 1099 | switch (mode) { | 1153 | |
| 1100 | case x86_64_mode_integer: | 1154 | for(start=0; start < size; start += 8) { |
| 1101 | if (size > 8) { | 1155 | if ((ty->t & VT_BTYPE) == VT_STRUCT) { |
| 1102 | *reg_count = 2; | 1156 | mode = classify_x86_64_arg_eightbyte(ty, start); |
| 1103 | ret_t = VT_QLONG; | | |
| 1104 | } else { | 1157 | } else { |
| 1105 | *reg_count = 1; | 1158 | mode = classify_x86_64_inner(ty, 0, 0, size); |
| 1106 | ret_t = (size > 4) ? VT_LLONG : VT_INT; | | |
| 1107 | } | 1159 | } |
| 1108 | break; | | |
| 1109 | | | |
| 1110 | case x86_64_mode_x87: | | |
| 1111 | *reg_count = 1; | | |
| 1112 | ret_t = VT_LDOUBLE; | | |
| 1113 | break; | | |
| 1114 | | 1160 | |
| 1115 | case x86_64_mode_sse: | 1161 | if (mode == x86_64_mode_integer) { |
| 1116 | if (size > 8) { | 1162 | if (args) |
| 1117 | *reg_count = 2; | 1163 | args->ireg[ireg++] = start; |
| 1118 | ret_t = VT_QFLOAT; | 1164 | ret_t = (size > 4) ? VT_LLONG : VT_INT; |
| 1119 | } else { | 1165 | } else if (mode == x86_64_mode_sse) { |
| 1120 | *reg_count = 1; | 1166 | if (args) |
| | 1167 | args->freg[freg++] = start; |
| 1121 | ret_t = (size > 4) ? VT_DOUBLE : VT_FLOAT; | 1168 | ret_t = (size > 4) ? VT_DOUBLE : VT_FLOAT; |
| | 1169 | } else { |
| | 1170 | ret_t = VT_LDOUBLE; |
| 1122 | } | 1171 | } |
| 1123 | break; | | |
| 1124 | default: break; /* nothing to be done for x86_64_mode_memory and x86_64_mode_none*/ | | |
| 1125 | } | 1172 | } |
| 1126 | } | 1173 | } |
| 1127 | } | 1174 | } |
| 1128 | | 1175 | |
| 1129 | if (ret) { | 1176 | if (ret) { |
| 1130 | ret->ref = NULL; | 1177 | ret->ref = NULL; |
| 1131 | ret->t = ret_t; | 1178 | ret->t = ret_t; |
| 1132 | } | 1179 | } |
| 1133 | | 1180 | |
| 1134 | return mode; | 1181 | return mode; |
| 1135 | } | 1182 | } |
| 1136 | | 1183 | |
| @@ -1140,8 +1187,8 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) · 3 unmodified lines |
| 1140 | enum __va_arg_type { | 1187 | enum __va_arg_type { |
| 1141 | __va_gen_reg, __va_float_reg, __va_stack | 1188 | __va_gen_reg, __va_float_reg, __va_stack |
| 1142 | }; | 1189 | }; |
| 1143 | int size, align, reg_count; | 1190 | int size, align; |
| 1144 | X86_64_Mode mode = classify_x86_64_arg(ty, NULL, &size, &align, ®_count); | 1191 | X86_64_Mode mode = classify_x86_64_arg(ty, NULL, &size, &align, NULL); |
| 1145 | switch (mode) { | 1192 | switch (mode) { |
| 1146 | default: return __va_stack; | 1193 | default: return __va_stack; |
| 1147 | case x86_64_mode_integer: return __va_gen_reg; | 1194 | case x86_64_mode_integer: return __va_gen_reg; |
| @@ -1149,14 +1196,57 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) · 1 unmodified line |
| 1149 | } | 1196 | } |
| 1150 | } | 1197 | } |
| 1151 | | 1198 | |
| 1152 | /* Return the number of registers needed to return the struct, or 0 if | 1199 | static int regargs_iregs(RegArgs *args) |
| 1153 | returning via struct pointer. */ | | |
| 1154 | ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) | | |
| 1155 | { | 1200 | { |
| 1156 | int size, align, reg_count; | 1201 | int i; |
| | 1202 | int ret = 0; |
| | 1203 | for(i=0; i<REG_ARGS_MAX; i++) { |
| | 1204 | if(args->ireg[i] != -1) |
| | 1205 | ret++; |
| | 1206 | } |
| | 1207 | |
| | 1208 | return ret; |
| | 1209 | } |
| | 1210 | |
| | 1211 | static int regargs_fregs(RegArgs *args) |
| | 1212 | { |
| | 1213 | int i; |
| | 1214 | int ret = 0; |
| | 1215 | for(i=0; i<REG_ARGS_MAX; i++) { |
| | 1216 | if(args->freg[i] != -1) |
| | 1217 | ret++; |
| | 1218 | } |
| | 1219 | |
| | 1220 | return ret; |
| | 1221 | } |
| | 1222 | |
| | 1223 | /* Count the total number of registers used by args */ |
| | 1224 | ST_FUNC int regargs_nregs(RegArgs *args) |
| | 1225 | { |
| | 1226 | int i; |
| | 1227 | int ret = 0; |
| | 1228 | for(i=0; i<REG_ARGS_MAX; i++) { |
| | 1229 | if(args->ireg[i] != -1) |
| | 1230 | ret++; |
| | 1231 | |
| | 1232 | if(args->freg[i] != -1) |
| | 1233 | ret++; |
| | 1234 | } |
| | 1235 | |
| | 1236 | return ret; |
| | 1237 | } |
| | 1238 | |
| | 1239 | ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args) |
| | 1240 | { |
| | 1241 | int size, align; |
| | 1242 | X86_64_Mode mode; |
| 1157 | *ret_align = 1; // Never have to re-align return values for x86-64 | 1243 | *ret_align = 1; // Never have to re-align return values for x86-64 |
| 1158 | *regsize = 8; | 1244 | *regsize = 8; |
| 1159 | return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) != x86_64_mode_memory); | 1245 | |
| | 1246 | mode = classify_x86_64_arg(vt, ret, &size, &align, args); |
| | 1247 | |
| | 1248 | return mode != x86_64_mode_memory && |
| | 1249 | mode != x86_64_mode_none; |
| 1160 | } | 1250 | } |
| 1161 | | 1251 | |
| 1162 | #define REGN 6 | 1252 | #define REGN 6 |
| @@ -1179,18 +1269,28 @@ void gfunc_call(int nb_args) · 16 unmodified lines |
| 1179 | { | 1269 | { |
| 1180 | X86_64_Mode mode; | 1270 | X86_64_Mode mode; |
| 1181 | CType type; | 1271 | CType type; |
| 1182 | int size, align, r, args_size, stack_adjust, run_start, run_end, i, reg_count; | 1272 | int size, align, r, args_size, stack_adjust, run_start, run_end, i; |
| 1183 | int nb_reg_args = 0; | 1273 | int nb_reg_args = 0; |
| 1184 | int nb_sse_args = 0; | 1274 | int nb_sse_args = 0; |
| 1185 | int sse_reg, gen_reg; | 1275 | int sse_reg = 0, gen_reg = 0; |
| | 1276 | RegArgs *reg_args = alloca(nb_args * sizeof *reg_args); |
| 1186 | | 1277 | |
| 1187 | /* calculate the number of integer/float register arguments */ | 1278 | /* calculate the number of integer/float register arguments */ |
| 1188 | for(i = 0; i < nb_args; i++) { | 1279 | for(i = nb_args - 1; i >= 0; i--) { |
| 1189 | mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); | 1280 | int fregs, iregs; |
| 1190 | if (mode == x86_64_mode_sse) | 1281 | mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_args[i]); |
| 1191 | nb_sse_args += reg_count; | 1282 | fregs = regargs_fregs(®_args[i]); |
| 1192 | else if (mode == x86_64_mode_integer) | 1283 | iregs = regargs_iregs(®_args[i]); |
| 1193 | nb_reg_args += reg_count; | 1284 | |
| | 1285 | nb_sse_args += fregs; |
| | 1286 | nb_reg_args += iregs; |
| | 1287 | |
| | 1288 | if (sse_reg + fregs > 8 || gen_reg + iregs > REGN) { |
| | 1289 | regargs_init(®_args[i]); |
| | 1290 | } else { |
| | 1291 | sse_reg += fregs; |
| | 1292 | gen_reg += iregs; |
| | 1293 | } |
| 1194 | } | 1294 | } |
| 1195 | | 1295 | |
| 1196 | /* arguments are collected in runs. Each run is a collection of 8-byte aligned arguments | 1296 | /* arguments are collected in runs. Each run is a collection of 8-byte aligned arguments |
| @@ -1209,27 +1309,14 @@ void gfunc_call(int nb_args) · 12 unmodified lines |
| 1209 | run_end = nb_args; | 1309 | run_end = nb_args; |
| 1210 | stack_adjust = 0; | 1310 | stack_adjust = 0; |
| 1211 | for(i = run_start; (i < nb_args) && (run_end == nb_args); i++) { | 1311 | for(i = run_start; (i < nb_args) && (run_end == nb_args); i++) { |
| 1212 | mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); | 1312 | int stack = regargs_nregs(®_args[i]) == 0; |
| 1213 | switch (mode) { | 1313 | classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, NULL); |
| 1214 | case x86_64_mode_memory: | 1314 | |
| 1215 | case x86_64_mode_x87: | 1315 | if (stack) { |
| 1216 | stack_arg: | | |
| 1217 | if (align == 16) | 1316 | if (align == 16) |
| 1218 | run_end = i; | 1317 | run_end = i; |
| 1219 | else | 1318 | else |
| 1220 | stack_adjust += size; | 1319 | stack_adjust += size; |
| 1221 | break; | | |
| 1222 | | | |
| 1223 | case x86_64_mode_sse: | | |
| 1224 | sse_reg -= reg_count; | | |
| 1225 | if (sse_reg + reg_count > 8) goto stack_arg; | | |
| 1226 | break; | | |
| 1227 | | | |
| 1228 | case x86_64_mode_integer: | | |
| 1229 | gen_reg -= reg_count; | | |
| 1230 | if (gen_reg + reg_count > REGN) goto stack_arg; | | |
| 1231 | break; | | |
| 1232 | default: break; /* nothing to be done for x86_64_mode_none */ | | |
| 1233 | } | 1320 | } |
| 1234 | } | 1321 | } |
| 1235 | | 1322 | |
| @@ -1249,44 +1336,38 @@ void gfunc_call(int nb_args) · 13 unmodified lines |
| 1249 | } | 1336 | } |
| 1250 | | 1337 | |
| 1251 | for(i = run_start; i < run_end;) { | 1338 | for(i = run_start; i < run_end;) { |
| | 1339 | int arg_stored = regargs_nregs(®_args[i]) == 0; |
| | 1340 | SValue tmp; |
| | 1341 | RegArgs args; |
| | 1342 | |
| | 1343 | if (!arg_stored) { |
| | 1344 | ++i; |
| | 1345 | continue; |
| | 1346 | } |
| | 1347 | |
| 1252 | /* Swap argument to top, it will possibly be changed here, | 1348 | /* Swap argument to top, it will possibly be changed here, |
| 1253 | and might use more temps. At the end of the loop we keep | 1349 | and might use more temps. At the end of the loop we keep |
| 1254 | in on the stack and swap it back to its original position | 1350 | in on the stack and swap it back to its original position |
| 1255 | if it is a register. */ | 1351 | if it is a register. */ |
| 1256 | SValue tmp = vtop[0]; | 1352 | tmp = vtop[0]; |
| 1257 | int arg_stored = 1; | | |
| 1258 | | | |
| 1259 | vtop[0] = vtop[-i]; | 1353 | vtop[0] = vtop[-i]; |
| 1260 | vtop[-i] = tmp; | 1354 | vtop[-i] = tmp; |
| 1261 | mode = classify_x86_64_arg(&vtop->type, NULL, &size, &align, ®_count); | | |
| 1262 | | 1355 | |
| | 1356 | classify_x86_64_arg(&vtop->type, NULL, &size, &align, &args); |
| | 1357 | |
| 1263 | switch (vtop->type.t & VT_BTYPE) { | 1358 | switch (vtop->type.t & VT_BTYPE) { |
| 1264 | case VT_STRUCT: | 1359 | case VT_STRUCT: |
| 1265 | if (mode == x86_64_mode_sse) { | 1360 | /* allocate the necessary size on stack */ |
| 1266 | if (sse_reg > 8) | 1361 | o(0x48); |
| 1267 | sse_reg -= reg_count; | 1362 | oad(0xec81, size); /* sub $xxx, %rsp */ |
| 1268 | else | 1363 | /* generate structure store */ |
| 1269 | arg_stored = 0; | 1364 | r = get_reg(RC_INT); |
| 1270 | } else if (mode == x86_64_mode_integer) { | 1365 | orex(1, r, 0, 0x89); /* mov %rsp, r */ |
| 1271 | if (gen_reg > REGN) | 1366 | o(0xe0 + REG_VALUE(r)); |
| 1272 | gen_reg -= reg_count; | 1367 | vset(&vtop->type, r | VT_LVAL, 0); |
| 1273 | else | 1368 | vswap(); |
| 1274 | arg_stored = 0; | 1369 | vstore(); |
| 1275 | } | 1370 | args_size += size; |
| 1276 | | | |
| 1277 | if (arg_stored) { | | |
| 1278 | /* allocate the necessary size on stack */ | | |
| 1279 | o(0x48); | | |
| 1280 | oad(0xec81, size); /* sub $xxx, %rsp */ | | |
| 1281 | /* generate structure store */ | | |
| 1282 | r = get_reg(RC_INT); | | |
| 1283 | orex(1, r, 0, 0x89); /* mov %rsp, r */ | | |
| 1284 | o(0xe0 + REG_VALUE(r)); | | |
| 1285 | vset(&vtop->type, r | VT_LVAL, 0); | | |
| 1286 | vswap(); | | |
| 1287 | vstore(); | | |
| 1288 | args_size += size; | | |
| 1289 | } | | |
| 1290 | break; | 1371 | break; |
| 1291 | | 1372 | |
| 1292 | case VT_LDOUBLE: | 1373 | case VT_LDOUBLE: |
| @@ -1295,57 +1376,43 @@ void gfunc_call(int nb_args) · 2 unmodified lines |
| 1295 | | 1376 | |
| 1296 | case VT_FLOAT: | 1377 | case VT_FLOAT: |
| 1297 | case VT_DOUBLE: | 1378 | case VT_DOUBLE: |
| 1298 | assert(mode == x86_64_mode_sse); | 1379 | r = gv(RC_FLOAT); |
| 1299 | if (sse_reg > 8) { | 1380 | o(0x50); /* push $rax */ |
| 1300 | --sse_reg; | 1381 | /* movq %xmmN, (%rsp) */ |
| 1301 | r = gv(RC_FLOAT); | 1382 | o(0xd60f66); |
| 1302 | o(0x50); /* push $rax */ | 1383 | o(0x04 + REG_VALUE(r)*8); |
| 1303 | /* movq %xmmN, (%rsp) */ | 1384 | o(0x24); |
| 1304 | o(0xd60f66); | 1385 | args_size += size; |
| 1305 | o(0x04 + REG_VALUE(r)*8); | | |
| 1306 | o(0x24); | | |
| 1307 | args_size += size; | | |
| 1308 | } else { | | |
| 1309 | arg_stored = 0; | | |
| 1310 | } | | |
| 1311 | break; | 1386 | break; |
| 1312 | | 1387 | |
| 1313 | default: | 1388 | default: |
| 1314 | assert(mode == x86_64_mode_integer); | | |
| 1315 | /* simple type */ | 1389 | /* simple type */ |
| 1316 | /* XXX: implicit cast ? */ | 1390 | /* XXX: implicit cast ? */ |
| 1317 | if (gen_reg > REGN) { | 1391 | --gen_reg; |
| 1318 | --gen_reg; | 1392 | r = gv(RC_INT); |
| 1319 | r = gv(RC_INT); | 1393 | orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */ |
| 1320 | orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */ | 1394 | args_size += size; |
| 1321 | args_size += size; | | |
| 1322 | } else { | | |
| 1323 | arg_stored = 0; | | |
| 1324 | } | | |
| 1325 | break; | 1395 | break; |
| 1326 | } | 1396 | } |
| 1327 | | 1397 | |
| 1328 | /* And swap the argument back to it's original position. */ | 1398 | /* And swap the argument back to its original position. */ |
| 1329 | tmp = vtop[0]; | 1399 | tmp = vtop[0]; |
| 1330 | vtop[0] = vtop[-i]; | 1400 | vtop[0] = vtop[-i]; |
| 1331 | vtop[-i] = tmp; | 1401 | vtop[-i] = tmp; |
| 1332 | | 1402 | |
| 1333 | if (arg_stored) { | 1403 | vrotb(i+1); |
| 1334 | vrotb(i+1); | 1404 | assert((vtop->type.t == tmp.type.t) && (vtop->r == tmp.r)); |
| 1335 | assert((vtop->type.t == tmp.type.t) && (vtop->r == tmp.r)); | 1405 | vpop(); |
| 1336 | vpop(); | 1406 | memmove(reg_args + i, reg_args + i + 1, (nb_args - i - 1) * sizeof *reg_args); |
| 1337 | --nb_args; | 1407 | --nb_args; |
| 1338 | --run_end; | 1408 | --run_end; |
| 1339 | } else { | | |
| 1340 | ++i; | | |
| 1341 | } | | |
| 1342 | } | 1409 | } |
| 1343 | | 1410 | |
| 1344 | /* handle 16 byte aligned arguments at end of run */ | 1411 | /* handle 16 byte aligned arguments at end of run */ |
| 1345 | run_start = i = run_end; | 1412 | run_start = i = run_end; |
| 1346 | while (i < nb_args) { | 1413 | while (i < nb_args) { |
| 1347 | /* Rotate argument to top since it will always be popped */ | 1414 | /* Rotate argument to top since it will always be popped */ |
| 1348 | mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); | 1415 | mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, NULL); |
| 1349 | if (align != 16) | 1416 | if (align != 16) |
| 1350 | break; | 1417 | break; |
| 1351 | | 1418 | |
| @@ -1375,13 +1442,24 @@ void gfunc_call(int nb_args) · 23 unmodified lines |
| 1375 | } | 1442 | } |
| 1376 | | 1443 | |
| 1377 | vpop(); | 1444 | vpop(); |
| | 1445 | memmove(reg_args + i, reg_args + i + 1, (nb_args - i - 1) * sizeof *reg_args); |
| 1378 | --nb_args; | 1446 | --nb_args; |
| 1379 | } | 1447 | } |
| 1380 | } | 1448 | } |
| 1381 | | 1449 | |
| 1382 | /* XXX This should be superfluous. */ | 1450 | /* XXX This should be superfluous. */ |
| 1383 | save_regs(0); /* save used temporary registers */ | 1451 | save_regs(0); /* save used temporary registers */ |
| 1384 | | 1452 | |
| | 1453 | /* recalculate the number of register arguments there actually |
| | 1454 | * are. This is slow but more obviously correct than using the |
| | 1455 | * old counts. */ |
| | 1456 | gen_reg = 0; |
| | 1457 | sse_reg = 0; |
| | 1458 | for(i = 0; i < nb_args; i++) { |
| | 1459 | gen_reg += regargs_iregs(®_args[i]); |
| | 1460 | sse_reg += regargs_fregs(®_args[i]); |
| | 1461 | } |
| | 1462 | |
| 1385 | /* then, we prepare register passing arguments. | 1463 | /* then, we prepare register passing arguments. |
| 1386 | Note that we cannot set RDX and RCX in this loop because gv() | 1464 | Note that we cannot set RDX and RCX in this loop because gv() |
| 1387 | may break these temporary registers. Let's use R10 and R11 | 1465 | may break these temporary registers. Let's use R10 and R11 |
| @@ -1389,40 +1467,62 @@ void gfunc_call(int nb_args) · 1 unmodified line |
| 1389 | assert(gen_reg <= REGN); | 1467 | assert(gen_reg <= REGN); |
| 1390 | assert(sse_reg <= 8); | 1468 | assert(sse_reg <= 8); |
| 1391 | for(i = 0; i < nb_args; i++) { | 1469 | for(i = 0; i < nb_args; i++) { |
| 1392 | mode = classify_x86_64_arg(&vtop->type, &type, &size, &align, ®_count); | 1470 | RegArgs args; |
| | 1471 | |
| | 1472 | args = reg_args[i]; |
| | 1473 | |
| 1393 | /* Alter stack entry type so that gv() knows how to treat it */ | 1474 | /* Alter stack entry type so that gv() knows how to treat it */ |
| 1394 | vtop->type = type; | 1475 | if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { |
| 1395 | if (mode == x86_64_mode_sse) { | 1476 | int k; |
| 1396 | if (reg_count == 2) { | 1477 | |
| 1397 | sse_reg -= 2; | 1478 | for(k=REG_ARGS_MAX-1; k>=0; k--) { |
| 1398 | gv(RC_FRET); /* Use pair load into xmm0 & xmm1 */ | 1479 | if (args.freg[k] == -1) |
| 1399 | if (sse_reg) { /* avoid redundant movaps %xmm0, %xmm0 */ | 1480 | continue; |
| 1400 | /* movaps %xmm0, %xmmN */ | 1481 | |
| 1401 | o(0x280f); | 1482 | sse_reg--; |
| 1402 | o(0xc0 + (sse_reg << 3)); | 1483 | assert(sse_reg >= 0); |
| 1403 | /* movaps %xmm1, %xmmN */ | 1484 | |
| 1404 | o(0x280f); | 1485 | vdup(); |
| 1405 | o(0xc1 + ((sse_reg+1) << 3)); | 1486 | vtop->type.t = VT_DOUBLE; |
| 1406 | } | 1487 | vtop->c.ull += args.freg[k]; |
| 1407 | } else { | 1488 | gv(RC_XMM0 << sse_reg); |
| 1408 | assert(reg_count == 1); | 1489 | vpop(); |
| | 1490 | } |
| | 1491 | for(k=REG_ARGS_MAX-1; k>=0; k--) { |
| | 1492 | int d; |
| | 1493 | if (args.ireg[k] == -1) |
| | 1494 | continue; |
| | 1495 | |
| | 1496 | gen_reg--; |
| | 1497 | |
| | 1498 | vdup(); |
| | 1499 | vtop->type.t = VT_LLONG; |
| | 1500 | vtop->c.ull += args.ireg[k]; |
| | 1501 | r = gv(RC_INT); |
| | 1502 | d = arg_prepare_reg(gen_reg); |
| | 1503 | orex(1,d,r,0x89); /* mov */ |
| | 1504 | o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); |
| | 1505 | vpop(); |
| | 1506 | } |
| | 1507 | } else { |
| | 1508 | /* XXX is it really necessary to set vtop->type? */ |
| | 1509 | classify_x86_64_arg(&vtop->type, &type, &size, &align, NULL); |
| | 1510 | vtop->type = type; |
| | 1511 | if (args.freg[0] != -1) { |
| 1409 | --sse_reg; | 1512 | --sse_reg; |
| 1410 | /* Load directly to register */ | 1513 | /* Load directly to register */ |
| 1411 | gv(RC_XMM0 << sse_reg); | 1514 | gv(RC_XMM0 << sse_reg); |
| 1412 | } | 1515 | } else if (args.ireg[0] != -1) { |
| 1413 | } else if (mode == x86_64_mode_integer) { | 1516 | int d; |
| 1414 | /* simple type */ | 1517 | /* simple type */ |
| 1415 | /* XXX: implicit cast ? */ | 1518 | /* XXX: implicit cast ? */ |
| 1416 | int d; | 1519 | gen_reg--; |
| 1417 | gen_reg -= reg_count; | 1520 | r = gv(RC_INT); |
| 1418 | r = gv(RC_INT); | 1521 | d = arg_prepare_reg(gen_reg); |
| 1419 | d = arg_prepare_reg(gen_reg); | 1522 | orex(1,d,r,0x89); /* mov */ |
| 1420 | orex(1,d,r,0x89); /* mov */ | 1523 | o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); |
| 1421 | o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); | 1524 | } else { |
| 1422 | if (reg_count == 2) { | 1525 | assert(0); |
| 1423 | d = arg_prepare_reg(gen_reg+1); | | |
| 1424 | orex(1,d,vtop->r2,0x89); /* mov */ | | |
| 1425 | o(0xc0 + REG_VALUE(vtop->r2) * 8 + REG_VALUE(d)); | | |
| 1426 | } | 1526 | } |
| 1427 | } | 1527 | } |
| 1428 | vtop--; | 1528 | vtop--; |
| @@ -1463,7 +1563,7 @@ static void push_arg_reg(int i) { · 34 unmodified lines |
| 1463 | void gfunc_prolog(CType *func_type) | 1563 | void gfunc_prolog(CType *func_type) |
| 1464 | { | 1564 | { |
| 1465 | X86_64_Mode mode; | 1565 | X86_64_Mode mode; |
| 1466 | int i, addr, align, size, reg_count; | 1566 | int i, addr, align, size; |
| 1467 | int param_addr = 0, reg_param_index, sse_param_index; | 1567 | int param_addr = 0, reg_param_index, sse_param_index; |
| 1468 | Sym *sym; | 1568 | Sym *sym; |
| 1469 | CType *type; | 1569 | CType *type; |
| @@ -1483,32 +1583,38 @@ void gfunc_prolog(CType *func_type) · 13 unmodified lines |
| 1483 | /* count the number of seen parameters */ | 1583 | /* count the number of seen parameters */ |
| 1484 | sym = func_type->ref; | 1584 | sym = func_type->ref; |
| 1485 | while ((sym = sym->next) != NULL) { | 1585 | while ((sym = sym->next) != NULL) { |
| | 1586 | RegArgs args; |
| | 1587 | |
| 1486 | type = &sym->type; | 1588 | type = &sym->type; |
| 1487 | mode = classify_x86_64_arg(type, NULL, &size, &align, ®_count); | 1589 | mode = classify_x86_64_arg(type, NULL, &size, &align, &args); |
| | 1590 | |
| 1488 | switch (mode) { | 1591 | switch (mode) { |
| 1489 | default: | 1592 | default: |
| 1490 | stack_arg: | 1593 | stack_arg: |
| 1491 | seen_stack_size = ((seen_stack_size + align - 1) & -align) + size; | 1594 | seen_stack_size = ((seen_stack_size + align - 1) & -align) + size; |
| 1492 | break; | 1595 | break; |
| 1493 | | 1596 | |
| 1494 | case x86_64_mode_integer: | 1597 | case x86_64_mode_integer: |
| 1495 | if (seen_reg_num + reg_count <= 8) { | 1598 | case x86_64_mode_sse: { |
| 1496 | seen_reg_num += reg_count; | 1599 | int stack = 0; |
| 1497 | } else { | 1600 | |
| | 1601 | seen_sse_num += regargs_fregs(&args); |
| | 1602 | seen_reg_num += regargs_iregs(&args); |
| | 1603 | |
| | 1604 | if (seen_reg_num > 8) { |
| 1498 | seen_reg_num = 8; | 1605 | seen_reg_num = 8; |
| 1499 | goto stack_arg; | 1606 | stack = 1; |
| 1500 | } | 1607 | } |
| 1501 | break; | 1608 | if (seen_sse_num > 8) { |
| 1502 | | | |
| 1503 | case x86_64_mode_sse: | | |
| 1504 | if (seen_sse_num + reg_count <= 8) { | | |
| 1505 | seen_sse_num += reg_count; | | |
| 1506 | } else { | | |
| 1507 | seen_sse_num = 8; | 1609 | seen_sse_num = 8; |
| 1508 | goto stack_arg; | 1610 | stack = 1; |
| 1509 | } | 1611 | } |
| | 1612 | |
| | 1613 | if (stack) |
| | 1614 | goto stack_arg; |
| 1510 | break; | 1615 | break; |
| 1511 | } | 1616 | } |
| | 1617 | } |
| 1512 | } | 1618 | } |
| 1513 | | 1619 | |
| 1514 | loc -= 16; | 1620 | loc -= 16; |
| @@ -1544,7 +1650,7 @@ void gfunc_prolog(CType *func_type) · 29 unmodified lines |
| 1544 | /* if the function returns a structure, then add an | 1650 | /* if the function returns a structure, then add an |
| 1545 | implicit pointer parameter */ | 1651 | implicit pointer parameter */ |
| 1546 | func_vt = sym->type; | 1652 | func_vt = sym->type; |
| 1547 | mode = classify_x86_64_arg(&func_vt, NULL, &size, &align, ®_count); | 1653 | mode = classify_x86_64_arg(&func_vt, NULL, &size, &align, NULL); |
| 1548 | if (mode == x86_64_mode_memory) { | 1654 | if (mode == x86_64_mode_memory) { |
| 1549 | push_arg_reg(reg_param_index); | 1655 | push_arg_reg(reg_param_index); |
| 1550 | func_vc = loc; | 1656 | func_vc = loc; |
| @@ -1552,19 +1658,40 @@ void gfunc_prolog(CType *func_type) · 1 unmodified line |
| 1552 | } | 1658 | } |
| 1553 | /* define parameters */ | 1659 | /* define parameters */ |
| 1554 | while ((sym = sym->next) != NULL) { | 1660 | while ((sym = sym->next) != NULL) { |
| | 1661 | RegArgs args; |
| | 1662 | int reg_count_integer = 0; |
| | 1663 | int reg_count_sse = 0; |
| | 1664 | int arg_stored = 1; |
| | 1665 | |
| 1555 | type = &sym->type; | 1666 | type = &sym->type; |
| 1556 | mode = classify_x86_64_arg(type, NULL, &size, &align, ®_count); | 1667 | mode = classify_x86_64_arg(type, NULL, &size, &align, &args); |
| | 1668 | reg_count_integer = regargs_iregs(&args); |
| | 1669 | reg_count_sse = regargs_fregs(&args); |
| | 1670 | |
| 1557 | switch (mode) { | 1671 | switch (mode) { |
| | 1672 | case x86_64_mode_integer: |
| 1558 | case x86_64_mode_sse: | 1673 | case x86_64_mode_sse: |
| 1559 | if (sse_param_index + reg_count <= 8) { | 1674 | if (reg_count_integer || reg_count_sse) { |
| | 1675 | if ((reg_count_sse == 0 || sse_param_index + reg_count_sse <= 8) && |
| | 1676 | (reg_count_integer == 0 || reg_param_index + reg_count_integer <= REGN)) { |
| | 1677 | /* argument fits into registers */ |
| | 1678 | arg_stored = 0; |
| | 1679 | } |
| | 1680 | } |
| | 1681 | |
| | 1682 | if (!arg_stored) { |
| 1560 | /* save arguments passed by register */ | 1683 | /* save arguments passed by register */ |
| 1561 | loc -= reg_count * 8; | 1684 | loc -= (reg_count_sse + reg_count_integer) * 8; |
| 1562 | param_addr = loc; | 1685 | param_addr = loc; |
| 1563 | for (i = 0; i < reg_count; ++i) { | 1686 | for (i = 0; i < reg_count_sse; ++i) { |
| 1564 | o(0xd60f66); /* movq */ | 1687 | o(0xd60f66); /* movq */ |
| 1565 | gen_modrm(sse_param_index, VT_LOCAL, NULL, param_addr + i*8); | 1688 | gen_modrm(sse_param_index, VT_LOCAL, NULL, param_addr + args.freg[i]); |
| 1566 | ++sse_param_index; | 1689 | ++sse_param_index; |
| 1567 | } | 1690 | } |
| | 1691 | for (i = 0; i < reg_count_integer; ++i) { |
| | 1692 | gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, param_addr + args.ireg[i]); |
| | 1693 | ++reg_param_index; |
| | 1694 | } |
| 1568 | } else { | 1695 | } else { |
| 1569 | addr = (addr + align - 1) & -align; | 1696 | addr = (addr + align - 1) & -align; |
| 1570 | param_addr = addr; | 1697 | param_addr = addr; |
| @@ -1578,23 +1705,6 @@ void gfunc_prolog(CType *func_type) · 7 unmodified lines |
| 1578 | param_addr = addr; | 1705 | param_addr = addr; |
| 1579 | addr += size; | 1706 | addr += size; |
| 1580 | break; | 1707 | break; |
| 1581 | | | |
| 1582 | case x86_64_mode_integer: { | | |
| 1583 | if (reg_param_index + reg_count <= REGN) { | | |
| 1584 | /* save arguments passed by register */ | | |
| 1585 | loc -= reg_count * 8; | | |
| 1586 | param_addr = loc; | | |
| 1587 | for (i = 0; i < reg_count; ++i) { | | |
| 1588 | gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, param_addr + i*8); | | |
| 1589 | ++reg_param_index; | | |
| 1590 | } | | |
| 1591 | } else { | | |
| 1592 | addr = (addr + align - 1) & -align; | | |
| 1593 | param_addr = addr; | | |
| 1594 | addr += size; | | |
| 1595 | } | | |
| 1596 | break; | | |
| 1597 | } | | |
| 1598 | default: break; /* nothing to be done for x86_64_mode_none */ | 1708 | default: break; /* nothing to be done for x86_64_mode_none */ |
| 1599 | } | 1709 | } |
| 1600 | sym_push(sym->v & ~SYM_FIELD, type, | 1710 | sym_push(sym->v & ~SYM_FIELD, type, |
| @@ -1631,7 +1741,7 @@ void gfunc_epilog(void) · 30 unmodified lines |
| 1631 | *bounds_ptr = 0; | 1741 | *bounds_ptr = 0; |
| 1632 | | 1742 | |
| 1633 | /* generate bound local allocation */ | 1743 | /* generate bound local allocation */ |
| 1634 | sym_data = get_sym_ref(&char_pointer_type, lbounds_section, | 1744 | sym_data = get_sym_ref(&char_pointer_type, lbounds_section, |
| 1635 | func_bound_offset, lbounds_section->data_offset); | 1745 | func_bound_offset, lbounds_section->data_offset); |
| 1636 | saved_ind = ind; | 1746 | saved_ind = ind; |
| 1637 | ind = func_bound_ind; | 1747 | ind = func_bound_ind; |
| @@ -2233,8 +2343,7 @@ ST_FUNC void gen_vla_alloc(CType *type, int align) { · 595 unmodified lines |
| 2233 | gfunc_call(1); | 2343 | gfunc_call(1); |
| 2234 | vset(type, REG_IRET, 0); | 2344 | vset(type, REG_IRET, 0); |
| 2235 | #else | 2345 | #else |
| 2236 | int r; | 2346 | int r = gv(RC_INT); /* allocation size */ |
| 2237 | r = gv(RC_INT); /* allocation size */ | | |
| 2238 | /* sub r,%rsp */ | 2347 | /* sub r,%rsp */ |
| 2239 | o(0x2b48); | 2348 | o(0x2b48); |
| 2240 | o(0xe0 | REG_VALUE(r)); | 2349 | o(0xe0 | REG_VALUE(r)); |