| @@ -25,6 +25,24 @@ · 24 unmodified lines |
| 25 | 25 | /* number of available registers */ |
| 26 | 26 | #define NB_REGS 25 |
| 27 | 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 | 47 | /* a register can belong to several classes. The classes must be |
| 30 | 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 | 771 | /* Return the number of registers needed to return the struct, or 0 if |
| 749 | 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 | 775 | int size, align; |
| 753 | 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 | 778 | size = type_size(vt, &align); |
| 756 | 779 | ret->ref = NULL; |
| 757 | 780 | if (size > 8) { |
| 758 | | − return 0; |
| 781 | + *args = 0; |
| 759 | 782 | } else if (size > 4) { |
| 760 | 783 | ret->t = VT_LLONG; |
| 761 | | − return 1; |
| 784 | + *args = 1; |
| 762 | 785 | } else if (size > 2) { |
| 763 | 786 | ret->t = VT_INT; |
| 764 | | − return 1; |
| 787 | + *args = 1; |
| 765 | 788 | } else if (size > 1) { |
| 766 | 789 | ret->t = VT_SHORT; |
| 767 | | − return 1; |
| 790 | + *args = 1; |
| 768 | 791 | } else { |
| 769 | 792 | ret->t = VT_BYTE; |
| 770 | | − return 1; |
| 793 | + *args = 1; |
| 771 | 794 | } |
| 795 | + |
| 796 | + return *args != 0; |
| 772 | 797 | } |
| 773 | 798 | |
| 774 | 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 | 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 | 1073 | X86_64_Mode mode; |
| 1047 | 1074 | Sym *f; |
| @@ -1067,8 +1094,10 @@ static X86_64_Mode classify_x86_64_inner(CType *ty) · 19 unmodified lines |
| 1067 | 1094 | f = ty->ref; |
| 1068 | 1095 | |
| 1069 | 1096 | mode = x86_64_mode_none; |
| 1070 | | − for (f = f->next; f; f = f->next) |
| 1071 | | − mode = classify_x86_64_merge(mode, classify_x86_64_inner(&f->type)); |
| 1097 | + while ((f = f->next) != NULL) { |
| 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 | 1102 | return mode; |
| 1074 | 1103 | } |
| @@ -1076,61 +1105,79 @@ static X86_64_Mode classify_x86_64_inner(CType *ty) · 1 unmodified line |
| 1076 | 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 | 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 | 1131 | int size, align, ret_t = 0; |
| 1083 | | − |
| 1132 | + int ireg = 0, freg = 0; |
| 1133 | + |
| 1134 | + if (args) |
| 1135 | + regargs_init(args); |
| 1136 | + |
| 1084 | 1137 | if (ty->t & (VT_BITFIELD|VT_ARRAY)) { |
| 1085 | 1138 | *psize = 8; |
| 1086 | 1139 | *palign = 8; |
| 1087 | | − *reg_count = 1; |
| 1140 | + if (args) |
| 1141 | + args->ireg[ireg++] = 0; |
| 1088 | 1142 | ret_t = ty->t; |
| 1089 | 1143 | mode = x86_64_mode_integer; |
| 1090 | 1144 | } else { |
| 1091 | 1145 | size = type_size(ty, &align); |
| 1092 | 1146 | *psize = (size + 7) & ~7; |
| 1093 | 1147 | *palign = (align + 7) & ~7; |
| 1094 | | − |
| 1148 | + |
| 1095 | 1149 | if (size > 16) { |
| 1096 | 1150 | mode = x86_64_mode_memory; |
| 1097 | 1151 | } else { |
| 1098 | | − mode = classify_x86_64_inner(ty); |
| 1099 | | − switch (mode) { |
| 1100 | | − case x86_64_mode_integer: |
| 1101 | | − if (size > 8) { |
| 1102 | | − *reg_count = 2; |
| 1103 | | − ret_t = VT_QLONG; |
| 1152 | + int start; |
| 1153 | + |
| 1154 | + for(start=0; start < size; start += 8) { |
| 1155 | + if ((ty->t & VT_BTYPE) == VT_STRUCT) { |
| 1156 | + mode = classify_x86_64_arg_eightbyte(ty, start); |
| 1104 | 1157 | } else { |
| 1105 | | − *reg_count = 1; |
| 1106 | | − ret_t = (size > 4) ? VT_LLONG : VT_INT; |
| 1158 | + mode = classify_x86_64_inner(ty, 0, 0, size); |
| 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: |
| 1116 | | − if (size > 8) { |
| 1117 | | − *reg_count = 2; |
| 1118 | | − ret_t = VT_QFLOAT; |
| 1119 | | − } else { |
| 1120 | | − *reg_count = 1; |
| 1161 | + if (mode == x86_64_mode_integer) { |
| 1162 | + if (args) |
| 1163 | + args->ireg[ireg++] = start; |
| 1164 | + ret_t = (size > 4) ? VT_LLONG : VT_INT; |
| 1165 | + } else if (mode == x86_64_mode_sse) { |
| 1166 | + if (args) |
| 1167 | + args->freg[freg++] = start; |
| 1121 | 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 | 1176 | if (ret) { |
| 1130 | 1177 | ret->ref = NULL; |
| 1131 | 1178 | ret->t = ret_t; |
| 1132 | 1179 | } |
| 1133 | | − |
| 1180 | + |
| 1134 | 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 | 1187 | enum __va_arg_type { |
| 1141 | 1188 | __va_gen_reg, __va_float_reg, __va_stack |
| 1142 | 1189 | }; |
| 1143 | | − int size, align, reg_count; |
| 1144 | | − X86_64_Mode mode = classify_x86_64_arg(ty, NULL, &size, &align, ®_count); |
| 1190 | + int size, align; |
| 1191 | + X86_64_Mode mode = classify_x86_64_arg(ty, NULL, &size, &align, NULL); |
| 1145 | 1192 | switch (mode) { |
| 1146 | 1193 | default: return __va_stack; |
| 1147 | 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 |
| 1153 | | − returning via struct pointer. */ |
| 1154 | | −ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) |
| 1199 | +static int regargs_iregs(RegArgs *args) |
| 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 | 1243 | *ret_align = 1; // Never have to re-align return values for x86-64 |
| 1158 | 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 | 1252 | #define REGN 6 |
| @@ -1179,18 +1269,28 @@ void gfunc_call(int nb_args) · 16 unmodified lines |
| 1179 | 1269 | { |
| 1180 | 1270 | X86_64_Mode mode; |
| 1181 | 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 | 1273 | int nb_reg_args = 0; |
| 1184 | 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 | 1278 | /* calculate the number of integer/float register arguments */ |
| 1188 | | − for(i = 0; i < nb_args; i++) { |
| 1189 | | − mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); |
| 1190 | | − if (mode == x86_64_mode_sse) |
| 1191 | | − nb_sse_args += reg_count; |
| 1192 | | − else if (mode == x86_64_mode_integer) |
| 1193 | | − nb_reg_args += reg_count; |
| 1279 | + for(i = nb_args - 1; i >= 0; i--) { |
| 1280 | + int fregs, iregs; |
| 1281 | + mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_args[i]); |
| 1282 | + fregs = regargs_fregs(®_args[i]); |
| 1283 | + iregs = regargs_iregs(®_args[i]); |
| 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 | 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 | 1309 | run_end = nb_args; |
| 1210 | 1310 | stack_adjust = 0; |
| 1211 | 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); |
| 1213 | | − switch (mode) { |
| 1214 | | − case x86_64_mode_memory: |
| 1215 | | − case x86_64_mode_x87: |
| 1216 | | − stack_arg: |
| 1312 | + int stack = regargs_nregs(®_args[i]) == 0; |
| 1313 | + classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, NULL); |
| 1314 | + |
| 1315 | + if (stack) { |
| 1217 | 1316 | if (align == 16) |
| 1218 | 1317 | run_end = i; |
| 1219 | 1318 | else |
| 1220 | 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 | 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 | 1348 | /* Swap argument to top, it will possibly be changed here, |
| 1253 | 1349 | and might use more temps. At the end of the loop we keep |
| 1254 | 1350 | in on the stack and swap it back to its original position |
| 1255 | 1351 | if it is a register. */ |
| 1256 | | − SValue tmp = vtop[0]; |
| 1257 | | − int arg_stored = 1; |
| 1258 | | − |
| 1352 | + tmp = vtop[0]; |
| 1259 | 1353 | vtop[0] = vtop[-i]; |
| 1260 | 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 | 1358 | switch (vtop->type.t & VT_BTYPE) { |
| 1264 | 1359 | case VT_STRUCT: |
| 1265 | | − if (mode == x86_64_mode_sse) { |
| 1266 | | − if (sse_reg > 8) |
| 1267 | | − sse_reg -= reg_count; |
| 1268 | | − else |
| 1269 | | − arg_stored = 0; |
| 1270 | | − } else if (mode == x86_64_mode_integer) { |
| 1271 | | − if (gen_reg > REGN) |
| 1272 | | − gen_reg -= reg_count; |
| 1273 | | − else |
| 1274 | | − arg_stored = 0; |
| 1275 | | − } |
| 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 | | − } |
| 1360 | + /* allocate the necessary size on stack */ |
| 1361 | + o(0x48); |
| 1362 | + oad(0xec81, size); /* sub $xxx, %rsp */ |
| 1363 | + /* generate structure store */ |
| 1364 | + r = get_reg(RC_INT); |
| 1365 | + orex(1, r, 0, 0x89); /* mov %rsp, r */ |
| 1366 | + o(0xe0 + REG_VALUE(r)); |
| 1367 | + vset(&vtop->type, r | VT_LVAL, 0); |
| 1368 | + vswap(); |
| 1369 | + vstore(); |
| 1370 | + args_size += size; |
| 1290 | 1371 | break; |
| 1291 | 1372 | |
| 1292 | 1373 | case VT_LDOUBLE: |
| @@ -1295,57 +1376,43 @@ void gfunc_call(int nb_args) · 2 unmodified lines |
| 1295 | 1376 | |
| 1296 | 1377 | case VT_FLOAT: |
| 1297 | 1378 | case VT_DOUBLE: |
| 1298 | | − assert(mode == x86_64_mode_sse); |
| 1299 | | − if (sse_reg > 8) { |
| 1300 | | − --sse_reg; |
| 1301 | | − r = gv(RC_FLOAT); |
| 1302 | | − o(0x50); /* push $rax */ |
| 1303 | | − /* movq %xmmN, (%rsp) */ |
| 1304 | | − o(0xd60f66); |
| 1305 | | − o(0x04 + REG_VALUE(r)*8); |
| 1306 | | − o(0x24); |
| 1307 | | − args_size += size; |
| 1308 | | − } else { |
| 1309 | | − arg_stored = 0; |
| 1310 | | − } |
| 1379 | + r = gv(RC_FLOAT); |
| 1380 | + o(0x50); /* push $rax */ |
| 1381 | + /* movq %xmmN, (%rsp) */ |
| 1382 | + o(0xd60f66); |
| 1383 | + o(0x04 + REG_VALUE(r)*8); |
| 1384 | + o(0x24); |
| 1385 | + args_size += size; |
| 1311 | 1386 | break; |
| 1312 | 1387 | |
| 1313 | 1388 | default: |
| 1314 | | − assert(mode == x86_64_mode_integer); |
| 1315 | 1389 | /* simple type */ |
| 1316 | 1390 | /* XXX: implicit cast ? */ |
| 1317 | | − if (gen_reg > REGN) { |
| 1318 | | − --gen_reg; |
| 1319 | | − r = gv(RC_INT); |
| 1320 | | − orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */ |
| 1321 | | − args_size += size; |
| 1322 | | − } else { |
| 1323 | | − arg_stored = 0; |
| 1324 | | − } |
| 1391 | + --gen_reg; |
| 1392 | + r = gv(RC_INT); |
| 1393 | + orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */ |
| 1394 | + args_size += size; |
| 1325 | 1395 | break; |
| 1326 | 1396 | } |
| 1327 | | − |
| 1328 | | − /* And swap the argument back to it's original position. */ |
| 1397 | + |
| 1398 | + /* And swap the argument back to its original position. */ |
| 1329 | 1399 | tmp = vtop[0]; |
| 1330 | 1400 | vtop[0] = vtop[-i]; |
| 1331 | 1401 | vtop[-i] = tmp; |
| 1332 | 1402 | |
| 1333 | | − if (arg_stored) { |
| 1334 | | − vrotb(i+1); |
| 1335 | | − assert((vtop->type.t == tmp.type.t) && (vtop->r == tmp.r)); |
| 1336 | | − vpop(); |
| 1337 | | − --nb_args; |
| 1338 | | − --run_end; |
| 1339 | | − } else { |
| 1340 | | − ++i; |
| 1341 | | − } |
| 1403 | + vrotb(i+1); |
| 1404 | + assert((vtop->type.t == tmp.type.t) && (vtop->r == tmp.r)); |
| 1405 | + vpop(); |
| 1406 | + memmove(reg_args + i, reg_args + i + 1, (nb_args - i - 1) * sizeof *reg_args); |
| 1407 | + --nb_args; |
| 1408 | + --run_end; |
| 1342 | 1409 | } |
| 1343 | 1410 | |
| 1344 | 1411 | /* handle 16 byte aligned arguments at end of run */ |
| 1345 | 1412 | run_start = i = run_end; |
| 1346 | 1413 | while (i < nb_args) { |
| 1347 | 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 | 1416 | if (align != 16) |
| 1350 | 1417 | break; |
| 1351 | 1418 | |
| @@ -1375,13 +1442,24 @@ void gfunc_call(int nb_args) · 23 unmodified lines |
| 1375 | 1442 | } |
| 1376 | 1443 | |
| 1377 | 1444 | vpop(); |
| 1445 | + memmove(reg_args + i, reg_args + i + 1, (nb_args - i - 1) * sizeof *reg_args); |
| 1378 | 1446 | --nb_args; |
| 1379 | 1447 | } |
| 1380 | 1448 | } |
| 1381 | | − |
| 1449 | + |
| 1382 | 1450 | /* XXX This should be superfluous. */ |
| 1383 | 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 | 1463 | /* then, we prepare register passing arguments. |
| 1386 | 1464 | Note that we cannot set RDX and RCX in this loop because gv() |
| 1387 | 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 | 1467 | assert(gen_reg <= REGN); |
| 1390 | 1468 | assert(sse_reg <= 8); |
| 1391 | 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 | 1474 | /* Alter stack entry type so that gv() knows how to treat it */ |
| 1394 | | − vtop->type = type; |
| 1395 | | − if (mode == x86_64_mode_sse) { |
| 1396 | | − if (reg_count == 2) { |
| 1397 | | − sse_reg -= 2; |
| 1398 | | − gv(RC_FRET); /* Use pair load into xmm0 & xmm1 */ |
| 1399 | | − if (sse_reg) { /* avoid redundant movaps %xmm0, %xmm0 */ |
| 1400 | | − /* movaps %xmm0, %xmmN */ |
| 1401 | | − o(0x280f); |
| 1402 | | − o(0xc0 + (sse_reg << 3)); |
| 1403 | | − /* movaps %xmm1, %xmmN */ |
| 1404 | | − o(0x280f); |
| 1405 | | − o(0xc1 + ((sse_reg+1) << 3)); |
| 1406 | | − } |
| 1407 | | − } else { |
| 1408 | | − assert(reg_count == 1); |
| 1475 | + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { |
| 1476 | + int k; |
| 1477 | + |
| 1478 | + for(k=REG_ARGS_MAX-1; k>=0; k--) { |
| 1479 | + if (args.freg[k] == -1) |
| 1480 | + continue; |
| 1481 | + |
| 1482 | + sse_reg--; |
| 1483 | + assert(sse_reg >= 0); |
| 1484 | + |
| 1485 | + vdup(); |
| 1486 | + vtop->type.t = VT_DOUBLE; |
| 1487 | + vtop->c.ull += args.freg[k]; |
| 1488 | + gv(RC_XMM0 << sse_reg); |
| 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 | 1512 | --sse_reg; |
| 1410 | 1513 | /* Load directly to register */ |
| 1411 | 1514 | gv(RC_XMM0 << sse_reg); |
| 1412 | | − } |
| 1413 | | − } else if (mode == x86_64_mode_integer) { |
| 1414 | | − /* simple type */ |
| 1415 | | − /* XXX: implicit cast ? */ |
| 1416 | | − int d; |
| 1417 | | − gen_reg -= reg_count; |
| 1418 | | − r = gv(RC_INT); |
| 1419 | | − d = arg_prepare_reg(gen_reg); |
| 1420 | | − orex(1,d,r,0x89); /* mov */ |
| 1421 | | − o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); |
| 1422 | | − if (reg_count == 2) { |
| 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)); |
| 1515 | + } else if (args.ireg[0] != -1) { |
| 1516 | + int d; |
| 1517 | + /* simple type */ |
| 1518 | + /* XXX: implicit cast ? */ |
| 1519 | + gen_reg--; |
| 1520 | + r = gv(RC_INT); |
| 1521 | + d = arg_prepare_reg(gen_reg); |
| 1522 | + orex(1,d,r,0x89); /* mov */ |
| 1523 | + o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); |
| 1524 | + } else { |
| 1525 | + assert(0); |
| 1426 | 1526 | } |
| 1427 | 1527 | } |
| 1428 | 1528 | vtop--; |
| @@ -1463,7 +1563,7 @@ static void push_arg_reg(int i) { · 34 unmodified lines |
| 1463 | 1563 | void gfunc_prolog(CType *func_type) |
| 1464 | 1564 | { |
| 1465 | 1565 | X86_64_Mode mode; |
| 1466 | | − int i, addr, align, size, reg_count; |
| 1566 | + int i, addr, align, size; |
| 1467 | 1567 | int param_addr = 0, reg_param_index, sse_param_index; |
| 1468 | 1568 | Sym *sym; |
| 1469 | 1569 | CType *type; |
| @@ -1483,32 +1583,38 @@ void gfunc_prolog(CType *func_type) · 13 unmodified lines |
| 1483 | 1583 | /* count the number of seen parameters */ |
| 1484 | 1584 | sym = func_type->ref; |
| 1485 | 1585 | while ((sym = sym->next) != NULL) { |
| 1586 | + RegArgs args; |
| 1587 | + |
| 1486 | 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 | 1591 | switch (mode) { |
| 1489 | 1592 | default: |
| 1490 | 1593 | stack_arg: |
| 1491 | 1594 | seen_stack_size = ((seen_stack_size + align - 1) & -align) + size; |
| 1492 | 1595 | break; |
| 1493 | | − |
| 1596 | + |
| 1494 | 1597 | case x86_64_mode_integer: |
| 1495 | | − if (seen_reg_num + reg_count <= 8) { |
| 1496 | | − seen_reg_num += reg_count; |
| 1497 | | − } else { |
| 1598 | + case x86_64_mode_sse: { |
| 1599 | + int stack = 0; |
| 1600 | + |
| 1601 | + seen_sse_num += regargs_fregs(&args); |
| 1602 | + seen_reg_num += regargs_iregs(&args); |
| 1603 | + |
| 1604 | + if (seen_reg_num > 8) { |
| 1498 | 1605 | seen_reg_num = 8; |
| 1499 | | − goto stack_arg; |
| 1606 | + stack = 1; |
| 1500 | 1607 | } |
| 1501 | | − break; |
| 1502 | | − |
| 1503 | | − case x86_64_mode_sse: |
| 1504 | | − if (seen_sse_num + reg_count <= 8) { |
| 1505 | | − seen_sse_num += reg_count; |
| 1506 | | − } else { |
| 1608 | + if (seen_sse_num > 8) { |
| 1507 | 1609 | seen_sse_num = 8; |
| 1508 | | − goto stack_arg; |
| 1610 | + stack = 1; |
| 1509 | 1611 | } |
| 1612 | + |
| 1613 | + if (stack) |
| 1614 | + goto stack_arg; |
| 1510 | 1615 | break; |
| 1511 | 1616 | } |
| 1617 | + } |
| 1512 | 1618 | } |
| 1513 | 1619 | |
| 1514 | 1620 | loc -= 16; |
| @@ -1544,7 +1650,7 @@ void gfunc_prolog(CType *func_type) · 29 unmodified lines |
| 1544 | 1650 | /* if the function returns a structure, then add an |
| 1545 | 1651 | implicit pointer parameter */ |
| 1546 | 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 | 1654 | if (mode == x86_64_mode_memory) { |
| 1549 | 1655 | push_arg_reg(reg_param_index); |
| 1550 | 1656 | func_vc = loc; |
| @@ -1552,19 +1658,40 @@ void gfunc_prolog(CType *func_type) · 1 unmodified line |
| 1552 | 1658 | } |
| 1553 | 1659 | /* define parameters */ |
| 1554 | 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 | 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 | 1671 | switch (mode) { |
| 1672 | + case x86_64_mode_integer: |
| 1558 | 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 | 1683 | /* save arguments passed by register */ |
| 1561 | | − loc -= reg_count * 8; |
| 1684 | + loc -= (reg_count_sse + reg_count_integer) * 8; |
| 1562 | 1685 | param_addr = loc; |
| 1563 | | − for (i = 0; i < reg_count; ++i) { |
| 1686 | + for (i = 0; i < reg_count_sse; ++i) { |
| 1564 | 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 | 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 | 1695 | } else { |
| 1569 | 1696 | addr = (addr + align - 1) & -align; |
| 1570 | 1697 | param_addr = addr; |
| @@ -1578,23 +1705,6 @@ void gfunc_prolog(CType *func_type) · 7 unmodified lines |
| 1578 | 1705 | param_addr = addr; |
| 1579 | 1706 | addr += size; |
| 1580 | 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 | 1708 | default: break; /* nothing to be done for x86_64_mode_none */ |
| 1599 | 1709 | } |
| 1600 | 1710 | sym_push(sym->v & ~SYM_FIELD, type, |
| @@ -1631,7 +1741,7 @@ void gfunc_epilog(void) · 30 unmodified lines |
| 1631 | 1741 | *bounds_ptr = 0; |
| 1632 | 1742 | |
| 1633 | 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 | 1745 | func_bound_offset, lbounds_section->data_offset); |
| 1636 | 1746 | saved_ind = ind; |
| 1637 | 1747 | ind = func_bound_ind; |
| @@ -2233,8 +2343,7 @@ ST_FUNC void gen_vla_alloc(CType *type, int align) { · 595 unmodified lines |
| 2233 | 2343 | gfunc_call(1); |
| 2234 | 2344 | vset(type, REG_IRET, 0); |
| 2235 | 2345 | #else |
| 2236 | | − int r; |
| 2237 | | − r = gv(RC_INT); /* allocation size */ |
| 2346 | + int r = gv(RC_INT); /* allocation size */ |
| 2238 | 2347 | /* sub r,%rsp */ |
| 2239 | 2348 | o(0x2b48); |
| 2240 | 2349 | o(0xe0 | REG_VALUE(r)); |