虽然我们构建了生成最小化 DFA 所需的数据,但收集的属性尚未处理。我们需要把它们转换为统一的形式,并如下打包有用的信息。
main (continued)
6137 printf ("#include /"config.h/"/n");
6138 printf ("#include /"system.h/"/n");
6139 printf ("#include /"coretypes.h/"/n");
6140 printf ("#include /"tm.h/"/n");
6141 printf ("#include /"rtl.h/"/n");
6142 printf ("#include /"tm_p.h/"/n");
6143 printf ("#include /"insn-config.h/"/n");
6144 printf ("#include /"recog.h/"/n");
6145 printf ("#include /"regs.h/"/n");
6146 printf ("#include /"real.h/"/n");
6147 printf ("#include /"output.h/"/n");
6148 printf ("#include /"insn-attr.h/"/n");
6149 printf ("#include /"toplev.h/"/n");
6150 printf ("#include /"flags.h/"/n");
6151 printf ("#include /"function.h/"/n");
6152 printf ("/n");
6153 printf ("#define operands recog_data.operand/n/n");
6154
6155 /* Make `insn_alternatives'. */
6156 insn_alternatives = oballoc (insn_code_number * sizeof (int));
6157 for (id = defs ; id; id = id->next)
6158 if (id->insn_code >= 0)
6159 insn_alternatives [id->insn_code] = (1 << id->num_alternatives) - 1;
6160
6161 /* Make `insn_n_alternatives'. */
6162 insn_n_alternatives = oballoc (insn_code_number * sizeof (int));
6163 for (id = defs ; id; id = id->next)
6164 if (id->insn_code >= 0)
6165 insn_n_alternatives [id->insn_code] = id->num_alternatives;
6166
6167 /* Prepare to write out attribute subroutines by checking everything stored
6168 away and building the attribute cases. */
6169
6170 check_defs ();
上面在 6159 行, insn_alternatives 保存着,对于每个指令编码,一个位图,每个可能的替代对应其中的一个比特。而在 6162 行, insn_n_alternatives 保存着,对于每个指令编码,约束替代的数目。在 6170 行 check_defs 扫描所有的定义,检查有效性,并把所有的 SET_ATTR 及 SET_ATTR_ALTERNATIVE 表达式转换为对应的 SET 表达式。
首先,看一下 SET_ATTR 及 SET_ATTR_ALTERNATIVE 表达式,以下摘自注释
在 DEFINE_INSN 及 DEFINE_PEEPHOLE 最后的操作数中,这可以被用于,根据被匹配的替代,指出将被用在赋值的属性值。
以下的三个表达式是等效的:
(set (attr "att") (cond [(eq_attrq "alternative" "1") (const_string "a1")
(eq_attrq "alternative" "2") (const_string "a2")]
(const_string "a3")))
(set_attr_alternative "att" [(const_string "a1") (const_string "a2")
(const_string "a3")])
(set_attr "att" "a1,a2,a3")
check_defs 将把后两个形式转换为第一个形式。
1313 static void
1314 check_defs (void) in genattrtab.c
1315 {
1316 struct insn_def *id;
1317 struct attr_desc *attr;
1318 int i;
1319 rtx value;
1320
1321 for (id = defs ; id; id = id->next)
1322 {
1323 if (XVEC (id->def, id->vec_idx) == NULL)
1324 continue ;
1325
1326 for (i = 0; i < XVECLEN (id->def, id->vec_idx); i++)
1327 {
1328 value = XVECEXP (id->def, id->vec_idx, i);
1329 switch (GET_CODE (value))
1330 {
1331 case SET:
1332 if (GET_CODE (XEXP (value, 0)) != ATTR)
1333 {
1334 message_with_line (id->lineno, "bad attribute set");
1335 have_error = 1;
1336 value = NULL_RTX;
1337 }
1338 break ;
1339
1340 case SET_ATTR_ALTERNATIVE:
1341 value = convert_set_attr_alternative (value, id);
1342 break ;
1343
1344 case SET_ATTR:
1345 value = convert_set_attr (value, id);
1346 break ;
1347
1348 default :
1349 message_with_line (id->lineno, "invalid attribute code %s",
1350 GET_RTX_NAME (GET_CODE (value)));
1351 have_error = 1;
1352 value = NULL_RTX;
1353 }
1354 if (value == NULL_RTX)
1355 continue ;
1356
1357 if ((attr = find_attr (&XSTR (XEXP (value, 0), 0), 0)) == NULL)
1358 {
1359 message_with_line (id->lineno, "unknown attribute %s",
1360 XSTR (XEXP (value, 0), 0));
1361 have_error = 1;
1362 continue ;
1363 }
1364
1365 XVECEXP (id->def, id->vec_idx, i) = value;
1366 XEXP (value, 1) = check_attr_value (XEXP (value, 1), attr);
1367 }
1368 }
1369 }
上面在 1323 行, def 指向这个 insn_def 所属的 rtx 对象, vec_idx 显示属性向量在这个 def 中的位置,这个值是硬编码的。对于 define_insn ,它是 4 。对于 define_peephole ,它是 3 。对于 define_asm_attribute ,它是 0 。模式中的属性部分是可选的,如果不存在,意味着使用该属性的缺省值。
SET_ATTR_ALTERNATIVE 的 RTL 格式是 “ sE ” 。使用以下的例子,
(set_attr_alternative "att" [(const_string "a1") (const_string "a2")
(const_string "a3")])
其中“ att ”是格式的“ s ”部分,而“ […] ”是“ E ”部分,它是应该含有 3 个元素的向量。正如我们已知的,格式“ s ”对应 CONST_STRING 的 rtx 对象,而格式“ E ”对应 RTVEC 的 rtx 对象。
1244 static rtx
1245 convert_set_attr_alternative (rtx exp, struct insn_def *id) in genattrtab.c
1246 {
1247 int num_alt = id->num_alternatives;
1248 rtx condexp;
1249 int i;
1250
1251 if (XVECLEN (exp, 1) != num_alt)
1252 {
1253 message_with_line (id->lineno,
1254 "bad number of entries in SET_ATTR_ALTERNATIVE");
1255 have_error = 1;
1256 return NULL_RTX;
1257 }
1258
1259 /* Make a COND with all tests but the last. Select the last value via the
1260 default. */
1261 condexp = rtx_alloc (COND);
1262 XVEC (condexp, 0) = rtvec_alloc ((num_alt - 1) * 2);
1263
1264 for (i = 0; i < num_alt - 1; i++)
1265 {
1266 const char *p;
1267 p = attr_numeral (i);
1268
1269 XVECEXP (condexp, 0, 2 * i) = attr_eq (alternative_name , p);
1270 XVECEXP (condexp, 0, 2 * i + 1) = XVECEXP (exp, 1, i);
1271 }
1272
1273 XEXP (condexp, 1) = XVECEXP (exp, 1, i);
1274
1275 return attr_rtx (SET, attr_rtx (ATTR, XSTR (exp, 0)), condexp);
1276 }
SET_ATTR 的 RTL 格式是“ ss ”。所有以下的例子, (set_attr "att" "a1,a2,a3") 。其中,“ ss ”对应“ att ”,及“ a1, a2, a3” 。首先把这个 SET_ATTR 对象转换到对应的 SET_ATTR_ALTERNATIVE 对象。然后通过上面的 convert_set_attr_alternatives ,这个 SET_ATTR_ALTERNATIVE 对象进一步转换到 SET 对象。
1281 static rtx
1282 convert_set_attr (rtx exp, struct insn_def *id) in genattrtab.c
1283 {
1284 rtx newexp;
1285 const char *name_ptr;
1286 char *p;
1287 int n;
1288
1289 /* See how many alternative specified. */
1290 n = n_comma_elts (XSTR (exp, 1));
1291 if (n == 1)
1292 return attr_rtx (SET,
1293 attr_rtx (ATTR, XSTR (exp, 0)),
1294 attr_rtx (CONST_STRING, XSTR (exp, 1)));
1295
1296 newexp = rtx_alloc (SET_ATTR_ALTERNATIVE);
1297 XSTR (newexp, 0) = XSTR (exp, 0);
1298 XVEC (newexp, 1) = rtvec_alloc (n);
1299
1300 /* Process each comma-separated name. */
1301 name_ptr = XSTR (exp, 1);
1302 n = 0;
1303 while ((p = next_comma_elt (&name_ptr)) != NULL)
1304 XVECEXP (newexp, 1, n++) = attr_rtx (CONST_STRING, p);
1305
1306 return convert_set_attr_alternative (newexp, id);
1307 }
现在,因为由于处理 SET_ATTR_ALTERNATIVE 及 SET_ATTR 对象,某些属性被改变了,因此需要下面 6175 行的 check_attr_value 来再一次验证属性。
main (continued)
6172 for (i = 0; i < MAX_ATTRS_INDEX; i++)
6173 for (attr = attrs [i]; attr; attr = attr->next)
6174 attr->default_val->value
6175 = check_attr_value (attr->default_val->value, attr);
6176
6177 if (have_error )
6178 return FATAL_EXIT_CODE;
6179
6180 for (i = 0; i < MAX_ATTRS_INDEX; i++)
6181 for (attr = attrs [i]; attr; attr = attr->next)
6182 fill_attr (attr);
6183
6184 /* Construct extra attributes for `length'. */
6185 make_length_attrs ();
6186
6187 /* Perform any possible optimizations to speed up compilation. */
6188 optimize_attrs ();
对于数值属性,在其模式定义中,它必须具有空的 LIST-OF-VALUE 部分(至于 define_attr 的细节,可以参考 读入 DEFINE_ATTR模式 一节)。
对于这些数值属性, fill_attr 用于从模式中收集这些值。除此之外,对于所有的属性,这个函数将构建一个列表,它把所有使用同一个属性的指令链接起来。
2263 static void
2264 fill_attr (struct attr_desc *attr) in genattrtab.c
2265 {
2266 struct attr_value *av;
2267 struct insn_ent *ie;
2268 struct insn_def *id;
2269 int i;
2270 rtx value;
2271
2272 /* Don't fill constant attributes. The value is independent of
2273 any particular insn. */
2274 if (attr->is_const)
2275 return ;
2276
2277 for (id = defs ; id; id = id->next)
2278 {
2279 /* If no value is specified for this insn for this attribute, use the
2280 default. */
2281 value = NULL;
2282 if (XVEC (id->def, id->vec_idx))
2283 for (i = 0; i < XVECLEN (id->def, id->vec_idx); i++)
2284 if (! strcmp_check (XSTR ( XEXP (XVECEXP (id->def, id->vec_idx, i), 0), 0),
2285 attr->name))
2286 value = XEXP (XVECEXP (id->def, id->vec_idx, i), 1);
2287
2288 if (value == NULL)
2289 av = attr->default_val;
2290 else
2291 av = get_attr_value (value, attr, id->insn_code);
2292
2293 ie = oballoc (sizeof (struct insn_ent ));
2294 ie->insn_code = id->insn_code;
2295 ie->insn_index = id->insn_code;
2296 insert_insn_ent (av, ie);
2297 }
2298 }
在 fill_attr 的 2274 行 , 属性视为常量 , 如果其值是 SYMBOL_REF 或 CONST_INT 。在 2277 行, defs 已经链接了所有的 define_insn , define_peephole 及 define_asm_attributes (参见 gen_insn )。因此在 2283 行的 FOR 循环遍历模式的属性部分,并调用 get_attr_value 来向属性的值列表加入新发现的值(注意对于具有以星号( * )开头,由系统产生的属性,它们将以相同的值,为每个指令所添加。这是因为,首先没有指令会包含匹配这个名字的属性,其次,所有这些属性由 make_internal_attr 构建,并具有单个的值)。而在 2296 行, insert_insn_ent 的定义如下,它把使用相同属性值的指令链接起来。
2632 static void
2633 insert_insn_ent (struct attr_value *av, struct insn_ent *ie) in genattrtab.c
2634 {
2635 ie->next = av->first_insn;
2636 av->first_insn = ie;
2637 av->num_insns++;
2638 if (ie->insn_code == -1)
2639 av->has_asm_insn = 1;
2640
2641 num_insn_ents ++;
2642 }
insn_ent 构成了具有相同属性值指令的链表,它具有如下定义。
161 struct insn_ent in genattrtab.c
162 {
163 struct insn_ent *next; /* Next in chain. */
164 int insn_code; /* Instruction number. */
165 int insn_index; /* Index of definition in file */
166 int lineno; /* Line number. */
167 };
继续 main ,为了更好地了解 6185 行的 make_length_attr ,首先看一下以下摘自 gccint 的段落。
对于许多机器而言,它们提供了多种跳转指令,每个对应不同跳转偏移。在绝大多数情形下,汇编器将选择使用正确的指令。不过,当汇编器做不到这一点时, GCC 可以,当一个特殊的属性——‘ length ’属性被定义时。这个属性必须,通过在其‘ define_attr ’中指定一个空字符串,来定义为具有数字值。
就‘ length ’属性而言,允许在测试表达式中使用另外两个形式的算术项:
(match_dup N)
这指向当前指令操作数 N 的地址,它必须是一个‘ label_re. ’。
(pc)
这指向当前指令的地址。其他的用法把它作为下一个指令的地址可能更符合,不过因为当前指令的长度会被计入,这可能会让人困扰。
对于普通的指令,长度将由‘ length ’属性的值来决定。在‘ addr_vec ’及‘ addr_diff_vec ’指令模式中,其长度通过向量的数量乘以每个向量的大小来计算。
长度以可取址储存单元( byte )为单位来计量。
下面的宏可以用于细化长度计算:
ADJUST_INSN_LENGTH (INSN, LENGTH)
如果定义了,作为一个其所被应用的上下文中的一个函数,修改赋予指令 INSN 的长度。 LENGTH 是一个左值( lvalue ),它包含了该指令初始计算的长度,并将为该指令正确的长度所更新。
这个宏通常是不要求的。要求它的一个情形是 ROMP 。在这个机器中,一个‘ addr_vec ’指令的长度必须加上 2 ,来补偿可能要求对齐的这个事实。
返回‘ get_attr_length ’(‘ length ’属性的值)的函数,可以被输出函数所使用,来确定所要写出的跳转指令的形式,如下面例子所显示的。
作为一个可变长度跳转说明的例子,考虑 IBM 360 。如果我们采用约定,一个寄存器将被设置为一个函数的起始地址,我们可以,使用一个 4 字节指令,跳转到距起始地址 4k 内的 label 。否则,我们需要一个 6 字节序列,来从内存载入地址,然后跳转到这个地址。
在这样的一个机器上,一个用于一个跳转指令的模式可能被详细说明如下:
(define_insn "jump"
[(set (pc)
(label_ref (match_operand 0 "" "")))]
""
{
return (get_attr_length (insn) == 4
? "b %l0" : "l r15,=a(%l0); br r15");
}
[(set (attr "length")
(if_then_else (lt (minus (pc) (match_dup 0) (const_int 4096))
(const_int 4)
(const_int 6)))])
事实上,对于每个 define_insn , define_peephole 及 define_asm_attribute 模式,如果它不显式设置一个属性,它将使用该属性的缺省值。在这里 make_length 为每个模式,根据其“ length ”属性,构建了 3 个额外的属性。它们是:
*insn_default_length
这是在调用 shorten_branches 之前,由返回的 get_attr_length 指令的长度。在长度依赖于相对地址的情形下,使用最大可能的那个。
*insn_variable_length_p
这返回 1 ,如果该指令的长度依赖于相对地址;否则返回 0 。
*insn_current_length
仅当已知该指令具有一个可变长度时,这才被调用,并基于相对地址返回当前长度。
2379 static void
2380 make_length_attrs (void) in genattrtab.c
2381 {
2382 static const char *new_names[] =
2383 {
2384 "*insn_default_length",
2385 "*insn_variable_length_p",
2386 "*insn_current_length"
2387 };
2388 static rtx (*const no_address_fn[]) (rtx) = {identity_fn, zero_fn, zero_fn};
2389 static rtx (*const address_fn[]) (rtx) = {max_fn, one_fn, identity_fn};
2390 size_t i;
2391 struct attr_desc *length_attr, *new_attr;
2392 struct attr_value *av, *new_av;
2393 struct insn_ent *ie, *new_ie;
2394
2395 /* See if length attribute is defined. If so, it must be numeric. Make
2396 it special so we don't output anything for it. */
2397 length_attr = find_attr (&length_str , 0);
2398 if (length_attr == 0)
2399 return ;
2400
2401 if (! length_attr->is_numeric)
2402 fatal ("length attribute must be numeric");
2403
2404 length_attr->is_const = 0;
2405 length_attr->is_special = 1;
2406
2407 /* Make each new attribute, in turn. */
2408 for (i = 0; i < ARRAY_SIZE (new_names); i++)
2409 {
2410 make_internal_attr (new_names[i],
2411 substitute_address (length_attr->default_val->value,
2412 no_address_fn[i], address_fn[i]),
2413 ATTR_NONE);
2414 new_attr = find_attr (&new_names[i], 0);
2415 for (av = length_attr->first_value; av; av = av->next)
2416 for (ie = av->first_insn; ie; ie = ie->next)
2417 {
2418 new_av = get_attr_value ( substitute_address (av->value,
2419 no_address_fn[i],
2420 address_fn[i]),
2421 new_attr, ie->insn_code);
2422 new_ie = oballoc (sizeof (struct insn_ent ));
2423 new_ie->insn_code = ie->insn_code;
2424 new_ie->insn_index = ie->insn_index;
2425 insert_insn_ent (new_av, new_ie);
2426 }
2427 }
2428 }
上面在 2411 行, substitute_address ,给定一个表达式 exp ,查看它是否是一个具有检查指令相对位置(使用 MATCH_DUP 或 PC )测试的 COND 或 IF_THEN_ELSE 。如果是,使用通过把该表达式传递给 address_fn 所得到的结果来替代之。如果不是,但它是一个 COND 或 IF_THEN_ELSE ,在每个值上递归调用这个函数(包括缺省值)。否则,返回由应用在 exp 上的 no_address_fn 所返回的值。
2307 static rtx
2308 substitute_address (rtx exp, rtx (*no_address_fn) (rtx), in genattrtab.c
2309 rtx (*address_fn) (rtx))
2310 {
2311 int i;
2312 rtx newexp;
2313
2314 if (GET_CODE (exp) == COND)
2315 {
2316 /* See if any tests use addresses. */
2317 address_used = 0;
2318 for (i = 0; i < XVECLEN (exp, 0); i += 2)
2319 walk_attr_value (XVECEXP (exp, 0, i));
2320
2321 if (address_used )
2322 return (*address_fn) (exp);
2323
2324 /* Make a new copy of this COND, replacing each element. */
2325 newexp = rtx_alloc (COND);
2326 XVEC (newexp, 0) = rtvec_alloc (XVECLEN (exp, 0));
2327 for (i = 0; i < XVECLEN (exp, 0); i += 2)
2328 {
2329 XVECEXP (newexp, 0, i) = XVECEXP (exp, 0, i);
2330 XVECEXP (newexp, 0, i + 1)
2331 = substitute_address (XVECEXP (exp, 0, i + 1),
2332 no_address_fn, address_fn);
2333 }
2334
2335 XEXP (newexp, 1) = substitute_address ( XEXP (exp, 1),
2336 no_address_fn, address_fn);
2337
2338 return newexp;
2339 }
2340
2341 else if (GET_CODE (exp) == IF_THEN_ELSE)
2342 {
2343 address_used = 0;
2344 walk_attr_value ( XEXP (exp, 0));
2345 if (address_used )
2346 return (*address_fn) (exp);
2347
2348 return attr_rtx (IF_THEN_ELSE,
2349 substitute_address ( XEXP (exp, 0),
2350 no_address_fn, address_fn),
2351 substitute_address ( XEXP (exp, 1),
2352 no_address_fn, address_fn),
2353 substitute_address ( XEXP (exp, 2),
2354 no_address_fn, address_fn));
2355 }
2356
2357 return (*no_address_fn) (exp);
2358 }
walk_attr_value 扫描属性值,这可能是个条件值(一个例外是下面 4979 行的 ATTR_FLAG ),并记录将要求什么动作来对它进行条件测试。
4934 static void
4935 walk_attr_value (rtx exp) in genattrtab.c
4936 {
4937 int i, j;
4938 const char *fmt;
4939 RTX_CODE code;
4940
4941 if (exp == NULL)
4942 return ;
4943
4944 code = GET_CODE (exp);
4945 switch (code)
4946 {
4947 case SYMBOL_REF:
4948 if (! ATTR_IND_SIMPLIFIED_P (exp))
4949 /* Since this is an arbitrary expression, it can look at anything.
4950 However, constant expressions do not depend on any particular
4951 insn. */
4952 must_extract = must_constrain = 1;
4953 return ;
4954
4955 case MATCH_OPERAND:
4956 must_extract = 1;
4957 return ;
4958
4959 case EQ_ATTR_ALT:
4960 must_extract = must_constrain = 1;
4961 break ;
4962
4963 case EQ_ATTR:
4964 if (XSTR (exp, 0) == alternative_name )
4965 must_extract = must_constrain = 1;
4966 else if (strcmp_check (XSTR (exp, 0), length_str ) == 0)
4967 length_used = 1;
4968 return ;
4969
4970 case MATCH_DUP:
4971 must_extract = 1;
4972 address_used = 1;
4973 return ;
4974
4975 case PC:
4976 address_used = 1;
4977 return ;
4978
4979 case ATTR_FLAG:
4980 return ;
4981
4982 default :
4983 break ;
4984 }
4985
4986 for (i = 0, fmt = GET_RTX_FORMAT (code); i < GET_RTX_LENGTH (code); i++)
4987 switch (*fmt++)
4988 {
4989 case 'e':
4990 case 'u':
4991 walk_attr_value ( XEXP (exp, i));
4992 break ;
4993
4994 case 'E':
4995 if (XVEC (exp, i) != NULL)
4996 for (j = 0; j < XVECLEN (exp, i); j++)
4997 walk_attr_value (XVECEXP (exp, i, j));
4998 break ;
4999 }
5000 }
在 4952 行, must_extract 表示我们是否需要提取指令的操作数, must_constrain 表示我们是否必须计算编译器变量 which_alternative 。在 4967 行, length_used 表示,是否使用了一个 (eq_attr "length" ...) ,而在 4972 行, address_used 显示是否使用了一个地址表达式。
最后,对两种指令类型的结果显示如下。
no_address_fn result of fn on exp attribute attached
identity_fn exp "*insn_default_length"
zero_fn rtx object of 0 "*insn_variable_length_p"
zero_fn rtx object of 0 "*insn_current_length"
表 1 :为其它指令构建的属性
address_fn result of fn on exp attribute attached
max_fn rtx object of max value in exp "*insn_default_length"
one_fn rtx object of 1 "*insn_variable_length_p"
identify_fn exp "*insn_current_length"
表 2 :为相对位置指令构建的属性