注
本文以 riscv-tools 为基础进行分析 gcc 与平台之间的关系,及porting基础描述。
GCC 平台描述
GCC 使用 RTL 和 C 宏的结合来描述平台,后缀为 "md" 的文件(gcc/config/*/.md)定义了 Machine description 。它是 RTL 的子集,描述目标机模型的指令集和用于代码优化的指令属性、指令延迟特性及窥孔优化策略等辅助信息。编译后转换为 C 的结构、数组或函数,作为共用算法的参数。
其他平台特性信息用C的宏定义来表示,也作为共用算法的参数。宏定义的内容有存储器的定义、源语言数据类型的宽度、栈定义、函数调用参数/返回方式、寻址模式、汇编输出的符号定义、GCC命令参数,以及调试信息格式等。
GCC的抽象机是一套标准名(包括宏), 它定义了一套标准指令集,各个不同平台对标准指令名提供语义模板或库函数实现。当语法树节点被分析为某一标准指令名所表示的操作时,代入平台对此标准指令的解释。GCC各平台的目标机模型实现抽象机时所表现的功能特性,即GCC使用参数代入的方法将目标机的具体参数代入抽象机,然后操作抽象机。
编译系统的平台移植时,首先确定新平台将要实现的系统抽象机子集。并书写新平台的描述文件,然后交叉编译生成新平台的编译程序。
GCC 定义抽象机的指令集,它以操作类型来划分指令并建立标准操作表。标准操作表由一组子表组成,每个子表代表一类操作。
GCC 标准指令与平台指令的连接
在 gcc/gensupport.h
文件中定义了 optab 结构体,并通过 OPTAB_CL
等宏将 optabs.def
文件中定义的操作函数定义到 optab
枚举中,如下所示:
#define OPTAB_CL(name, pat, c, b, l) name,
#define OPTAB_CX(name, pat)
#define OPTAB_CD(name, pat) name,
#define OPTAB_NL(name, pat, c, b, s, l) name,
#define OPTAB_NC(name, pat, c) name,
#define OPTAB_NX(name, pat)
#define OPTAB_VL(name, pat, c, b, s, l) name,
#define OPTAB_VC(name, pat, c) name,
#define OPTAB_VX(name, pat)
#define OPTAB_DC(name, pat, c) name,
#define OPTAB_D(name, pat) name,
/* Enumerates all optabs. */
typedef enum optab_tag {
unknown_optab,
#include "optabs.def"
NUM_OPTABS
} optab;
而在 gcc/gensupport.c
文件中将所有 optabs.def
中的函数定义到 optabs
数组内,如下:
#define NS "NULL"
#define ZS "'\\0'"
#define OPTAB_CL(o, p, c, b, l) { #o, p, #b, ZS, #l, o, c, UNKNOWN, 1 },
#define OPTAB_CX(o, p) { #o, p, NULL, NULL, NULL, o, UNKNOWN, UNKNOWN, 1 },
#define OPTAB_CD(o, p) { #o, p, NS, ZS, NS, o, UNKNOWN, UNKNOWN, 2 },
#define OPTAB_NL(o, p, c, b, s, l) { #o, p, #b, #s, #l, o, c, c, 3 },
#define OPTAB_NC(o, p, c) { #o, p, NS, ZS, NS, o, c, c, 3 },
#define OPTAB_NX(o, p) { #o, p, NULL, NULL, NULL, o, UNKNOWN, UNKNOWN, 3 },
#define OPTAB_VL(o, p, c, b, s, l) { #o, p, #b, #s, #l, o, c, UNKNOWN, 3 },
#define OPTAB_VC(o, p, c) { #o, p, NS, ZS, NS, o, c, UNKNOWN, 3 },
#define OPTAB_VX(o, p) { #o, p, NULL, NULL, NULL, o, UNKNOWN, UNKNOWN, 3 },
#define OPTAB_DC(o, p, c) { #o, p, NS, ZS, NS, o, c, c, 4 },
#define OPTAB_D(o, p) { #o, p, NS, ZS, NS, o, UNKNOWN, UNKNOWN, 4 },
/* An array of all optabs. Note that the same optab can appear more
than once, with a different pattern. */
optab_def optabs[] = {
{ "unknown_optab", NULL, NS, ZS, NS, unknown_optab, UNKNOWN, UNKNOWN, 0 },
#include "optabs.def"
};
/* The number of entries in optabs[]. */
unsigned int num_optabs = ARRAY_SIZE (optabs);
/** 省略中间代码 */
/* Describes one entry in optabs.def. */
struct optab_def
{
/* The name of the optab (e.g. "add_optab"). */
const char *name;
/* The pattern that matching define_expands and define_insns have.
See the comment at the head of optabs.def for details. */
const char *pattern;
/* The initializers (in the form of C code) for the libcall_basename,
libcall_suffix and libcall_gen fields of (convert_)optab_libcall_d. */
const char *base;
const char *suffix;
const char *libcall;
/* The optab's enum value. */
unsigned int op;
/* The value returned by optab_to_code (OP). */
enum rtx_code fcode;
/* CODE if code_to_optab (CODE) should return OP, otherwise UNKNOWN. */
enum rtx_code rcode;
/* 1: conversion optabs with libcall data,
2: conversion optabs without libcall data,
3: non-conversion optabs with libcall data ("normal" and "overflow"
optabs in the optabs.def comment)
4: non-conversion optabs without libcall data ("direct" optabs). */
unsigned int kind;
};
在 gcc/optabs.def
文件中定义了 add_optab
和 sub_optab
等基础的标准表操作如下:
OPTAB_NL(add_optab, "add$P$a3", PLUS, "add", '3', gen_int_fp_fixed_libfunc)
OPTAB_NX(add_optab, "add$F$a3")
OPTAB_NX(add_optab, "add$Q$a3")
OPTAB_VL(addv_optab, "addv$I$a3", PLUS, "add", '3', gen_intv_fp_libfunc)
OPTAB_VX(addv_optab, "add$F$a3")
OPTAB_NL(ssadd_optab, "ssadd$Q$a3", SS_PLUS, "ssadd", '3', gen_signed_fixed_libfunc)
OPTAB_NL(usadd_optab, "usadd$Q$a3", US_PLUS, "usadd", '3', gen_unsigned_fixed_libfunc)
OPTAB_NL(sub_optab, "sub$P$a3", MINUS, "sub", '3', gen_int_fp_fixed_libfunc)
OPTAB_NX(sub_optab, "sub$F$a3")
OPTAB_NX(sub_optab, "sub$Q$a3")
OPTAB_VL(subv_optab, "subv$I$a3", MINUS, "sub", '3', gen_intv_fp_libfunc)
OPTAB_VX(subv_optab, "sub$F$a3")
OPTAB_NL(sssub_optab, "sssub$Q$a3", SS_MINUS, "sssub", '3', gen_signed_fixed_libfunc)
OPTAB_NL(ussub_optab, "ussub$Q$a3", US_MINUS, "ussub", '3', gen_unsigned_fixed_libfunc)
对于 add_optab
实际定义为
#define OPTAB_NL(o, p, c, b, s, l) { #o, p, #b, #s, #l, o, c, c, 3 },
OPTAB_NL(add_optab, "add$P$a3", PLUS, "add", '3', gen_int_fp_fixed_libfunc)
# 而 optab_def 定义如下:
# {name, pattern, base, suffix, libcall, op, fcode, rcode, kind}
{"add_optab", "add$P$a3", "add", "3", gen_int_fp_fixed_libfunc, add_optab, PLUS, PLUS, 3 }
因此,对于 add_optab
来说,主要实现 add
指令操作,其对应与 gcc/config/riscv/riscv.md
文件中的如下定义:
(define_insn "add<mode>3"
[(set (match_operand:ANYF 0 "register_operand" "=f")
(plus:ANYF (match_operand:ANYF 1 "register_operand" " f")
(match_operand:ANYF 2 "register_operand" " f")))]
"TARGET_HARD_FLOAT"
"fadd.<fmt>\t%0,%1,%2"
[(set_attr "type" "fadd")
(set_attr "mode" "<UNITMODE>")])
(define_insn "addsi3"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (match_operand:SI 1 "register_operand" " r,r")
(match_operand:SI 2 "arith_operand" " r,I")))]
""
{ return TARGET_64BIT ? "add%i2w\t%0,%1,%2" : "add%i2\t%0,%1,%2"; }
[(set_attr "type" "arith")
(set_attr "mode" "SI")])
(define_insn "adddi3"
[(set (match_operand:DI 0 "register_operand" "=r,r")
(plus:DI (match_operand:DI 1 "register_operand" " r,r")
(match_operand:DI 2 "arith_operand" " r,I")))]
"TARGET_64BIT"
"add%i2\t%0,%1,%2"
[(set_attr "type" "arith")
(set_attr "mode" "DI")])