Skip to content

Targets

targets

This file provides tools to describe toolchain targets.

Based on:

[1]: `RISC-V ABIs Specification, Document Version 1.0', Editors Kito Cheng and Jessica Clarke, RISC-V International, November 2022. https://github.com/riscv-non-isa/riscv-elf-psabi-doc/releases/tag/v1.0 [2]: “The RISC-V Instruction Set Manual, Volume I: User-Level ISA, Document Version 20191213”, Editors Andrew Waterman and Krste Asanovi´c, RISC-V Foundation, December 2019. https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMAFDQC/riscv-spec-20191213.pdf

ABISpec dataclass

This defines the ABI.

Largely based on chapter 2 of [1]

Source code in xdsl/backend/riscv/targets.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
@dataclass(frozen=True, unsafe_hash=True)
class ABISpec:
    """
    This defines the ABI.

    Largely based on chapter 2 of [1]
    """

    # various type bitwidths:
    int_width: int
    long_width: int
    pointer_width: int

    # The ABIs flen is allowed to smaller than the machine flen
    abi_flen: Literal[0, 32, 64, 128]
    """
    ABI_FLEN refers to the width of a floating-point register in the ABI. The ABI_FLEN
    must be no wider than the ISA’s FLEN. The ISA might have wider floating-point
    registers than the ABI.

    (cited from section 2.2 [1], p. 10)
    """

    # binary file format:
    file_format: str = "elf"
    """
    The output file format (for example of object files).

    "elf" is the default for most
    """

    # stack alignment:
    stack_alignment: int = 128
    """
    The stack grows downwards (towards lower addresses) and the stack pointer shall be
    aligned to a 128-bit boundary upon procedure entry. The first argument passed on the
    stack is located at offset zero of the stack pointer on function entry; following
    arguments are stored at correspondingly higher addresses.

    (cited from section 2.1 [1] p. 9)

    A supporting illustration:

    +--------------------------+ <--- stack "end", higher address
    |       In-use stack       |
    | (from calling functions) |
    +--------------------------+
    |    Padding if needed     |
    +--------------------------+
    |     stack argument n     |
    |     stack argument n-1   |
    |            ...           |
    |     stack argument 0     |
    +--------------------------+ <--- Stack pointer, aligned to 128 bits
    |           empty          |
    |                          |
    +--------------------------+ <--- stack "start", lower address

    This means `0(sp)` is stack argument 0, and `4*n(sp)` is stack argument n.
    """

int_width: int instance-attribute

long_width: int instance-attribute

pointer_width: int instance-attribute

abi_flen: Literal[0, 32, 64, 128] instance-attribute

ABI_FLEN refers to the width of a floating-point register in the ABI. The ABI_FLEN must be no wider than the ISA’s FLEN. The ISA might have wider floating-point registers than the ABI.

(cited from section 2.2 [1], p. 10)

file_format: str = 'elf' class-attribute instance-attribute

The output file format (for example of object files).

"elf" is the default for most

stack_alignment: int = 128 class-attribute instance-attribute

The stack grows downwards (towards lower addresses) and the stack pointer shall be aligned to a 128-bit boundary upon procedure entry. The first argument passed on the stack is located at offset zero of the stack pointer on function entry; following arguments are stored at correspondingly higher addresses.

(cited from section 2.1 [1] p. 9)

A supporting illustration:

+--------------------------+ <--- stack "end", higher address | In-use stack | | (from calling functions) | +--------------------------+ | Padding if needed | +--------------------------+ | stack argument n | | stack argument n-1 | | ... | | stack argument 0 | +--------------------------+ <--- Stack pointer, aligned to 128 bits | empty | | | +--------------------------+ <--- stack "start", lower address

This means 0(sp) is stack argument 0, and 4*n(sp) is stack argument n.

__init__(int_width: int, long_width: int, pointer_width: int, abi_flen: Literal[0, 32, 64, 128], file_format: str = 'elf', stack_alignment: int = 128) -> None

MachineArchSpec dataclass

Machine architecture spec, bitwidth, extensions, etc.

Source code in xdsl/backend/riscv/targets.py
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
@dataclass(frozen=True, unsafe_hash=True, init=False, repr=False)
class MachineArchSpec:
    """
    Machine architecture spec, bitwidth, extensions, etc.
    """

    xlen: int
    """
    Register size, basically 32/64, for RV32/RV64 respectively
    """
    flen: int
    """
    Floating point register width (0/32/64/128)
    """

    extensions: tuple[str, ...]
    """
    A list of extensions, fully expanded.

    RV32G would be: ["I", "M", "A", "F", "D", "Zifencei", "Zicsr"]
    """

    @property
    def spec_string(self) -> str:
        i = 0
        for i, e in enumerate(self.extensions):
            if len(e) > 1:
                break
        return "".join(self.extensions[:i]) + "_".join(self.extensions[i:])

    def __repr__(self):
        return f'MachineArchSpec("RV{self.xlen}{self.spec_string}")'

    def __init__(self, march: str):
        if not march.startswith("RV"):
            raise ValueError("Spec must start with RV...")

        match = re.fullmatch(
            r"RV(\d+)([A-Y]*)((Z[a-z]+)?(_Z[a-z]+)*)_?((X[a-z]+)?(_X[a-z]+)*)", march
        )
        if match is None:
            raise ValueError(f'Malformed march string: "{march}"')
        width_str, letters, exts, _, _, more_exts, _, _ = match.groups()

        # set bitwidth
        object.__setattr__(self, "xlen", int(width_str))

        # normalize extensions
        object.__setattr__(
            self,
            "extensions",
            _expand_isa_letters(
                list(letters)
                + ["Z" + z.lower().strip("_") for z in exts.split("Z")[1:]]
                + ["X" + x.lower().strip("_") for x in more_exts.split("X")[1:]]
            ),
        )

        # determine flen
        if "Q" in self.extensions:
            flen = 128
        elif "D" in self.extensions:
            flen = 64
        elif "F" in self.extensions:
            flen = 32
        else:
            flen = 0
        object.__setattr__(self, "flen", flen)

    def supports_mabi(self, abi: ABISpec) -> bool:
        """
        Implements checks lined out in section 2.4 of [2], p. 12
        """
        # check that abi flen is not larger than the march flen
        if abi.abi_flen > self.flen:
            return False
        # check that ilp32* is only used on RV32*
        # and lp64* is only used on RV64*
        if abi.pointer_width != self.xlen:
            return False
        if abi.long_width != self.xlen:
            return False
        # check that an int always fits in a single register
        if abi.int_width > self.xlen:
            return False
        return True

xlen: int instance-attribute

Register size, basically 32/64, for RV32/RV64 respectively

flen: int instance-attribute

Floating point register width (0/32/64/128)

extensions: tuple[str, ...] instance-attribute

A list of extensions, fully expanded.

RV32G would be: ["I", "M", "A", "F", "D", "Zifencei", "Zicsr"]

spec_string: str property

__repr__()

Source code in xdsl/backend/riscv/targets.py
163
164
def __repr__(self):
    return f'MachineArchSpec("RV{self.xlen}{self.spec_string}")'

__init__(march: str)

Source code in xdsl/backend/riscv/targets.py
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
def __init__(self, march: str):
    if not march.startswith("RV"):
        raise ValueError("Spec must start with RV...")

    match = re.fullmatch(
        r"RV(\d+)([A-Y]*)((Z[a-z]+)?(_Z[a-z]+)*)_?((X[a-z]+)?(_X[a-z]+)*)", march
    )
    if match is None:
        raise ValueError(f'Malformed march string: "{march}"')
    width_str, letters, exts, _, _, more_exts, _, _ = match.groups()

    # set bitwidth
    object.__setattr__(self, "xlen", int(width_str))

    # normalize extensions
    object.__setattr__(
        self,
        "extensions",
        _expand_isa_letters(
            list(letters)
            + ["Z" + z.lower().strip("_") for z in exts.split("Z")[1:]]
            + ["X" + x.lower().strip("_") for x in more_exts.split("X")[1:]]
        ),
    )

    # determine flen
    if "Q" in self.extensions:
        flen = 128
    elif "D" in self.extensions:
        flen = 64
    elif "F" in self.extensions:
        flen = 32
    else:
        flen = 0
    object.__setattr__(self, "flen", flen)

supports_mabi(abi: ABISpec) -> bool

Implements checks lined out in section 2.4 of [2], p. 12

Source code in xdsl/backend/riscv/targets.py
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
def supports_mabi(self, abi: ABISpec) -> bool:
    """
    Implements checks lined out in section 2.4 of [2], p. 12
    """
    # check that abi flen is not larger than the march flen
    if abi.abi_flen > self.flen:
        return False
    # check that ilp32* is only used on RV32*
    # and lp64* is only used on RV64*
    if abi.pointer_width != self.xlen:
        return False
    if abi.long_width != self.xlen:
        return False
    # check that an int always fits in a single register
    if abi.int_width > self.xlen:
        return False
    return True

TargetDefinition dataclass

Source code in xdsl/backend/riscv/targets.py
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
@dataclass
class TargetDefinition:
    abi: ABISpec
    """
    Target ABI (type bitwidth, stack alignment, argument passing)
    """

    march: MachineArchSpec
    """
    Machine architecture (handled by -march=RV...)
    """

    code_model: Literal["any", "low"] = "any"
    """
    Code model (usually handled by -mcmodel=med<model>):
    https://github.com/riscv-non-isa/riscv-toolchain-conventions#specifying-the-target-code-model-with--mcmodel

    low: The program and its statically defined symbols must lie within a single 2GiB
    address range, between the absolute addresses -2GiB and +2GiB. lui and addi pairs
    are used to generate addresses.

    any: The program and its statically defined symbols must lie within a single 4GiB
    address range. auipc and addi pairs are used to generate addresses.
    """

    def is_valid(self) -> bool:
        return self.march.supports_mabi(self.abi) and self.code_model in ("any", "low")

abi: ABISpec instance-attribute

Target ABI (type bitwidth, stack alignment, argument passing)

march: MachineArchSpec instance-attribute

Machine architecture (handled by -march=RV...)

code_model: Literal['any', 'low'] = 'any' class-attribute instance-attribute

Code model (usually handled by -mcmodel=med): https://github.com/riscv-non-isa/riscv-toolchain-conventions#specifying-the-target-code-model-with--mcmodel

low: The program and its statically defined symbols must lie within a single 2GiB address range, between the absolute addresses -2GiB and +2GiB. lui and addi pairs are used to generate addresses.

any: The program and its statically defined symbols must lie within a single 4GiB address range. auipc and addi pairs are used to generate addresses.

__init__(abi: ABISpec, march: MachineArchSpec, code_model: Literal['any', 'low'] = 'any') -> None

is_valid() -> bool

Source code in xdsl/backend/riscv/targets.py
246
247
def is_valid(self) -> bool:
    return self.march.supports_mabi(self.abi) and self.code_model in ("any", "low")

MAbi

Bases: Enum

Collection of named ABIs as per chapter 2.4 of [1].

ILP32E is omitted as the E extension is currently not ratified.

Source code in xdsl/backend/riscv/targets.py
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
class MAbi(Enum):
    """
    Collection of named ABIs as per chapter 2.4 of [1].

    ILP32E is omitted as the E extension is currently not ratified.
    """

    ILP32 = ABISpec(32, 32, 32, abi_flen=0)
    ILP32F = ABISpec(32, 32, 32, abi_flen=32)
    ILP32D = ABISpec(32, 32, 32, abi_flen=64)

    LP64 = ABISpec(32, 64, 64, abi_flen=0)
    LP64F = ABISpec(32, 64, 64, abi_flen=32)
    LP64D = ABISpec(32, 64, 64, abi_flen=64)
    LP64Q = ABISpec(32, 64, 64, abi_flen=128)

ILP32 = ABISpec(32, 32, 32, abi_flen=0) class-attribute instance-attribute

ILP32F = ABISpec(32, 32, 32, abi_flen=32) class-attribute instance-attribute

ILP32D = ABISpec(32, 32, 32, abi_flen=64) class-attribute instance-attribute

LP64 = ABISpec(32, 64, 64, abi_flen=0) class-attribute instance-attribute

LP64F = ABISpec(32, 64, 64, abi_flen=32) class-attribute instance-attribute

LP64D = ABISpec(32, 64, 64, abi_flen=64) class-attribute instance-attribute

LP64Q = ABISpec(32, 64, 64, abi_flen=128) class-attribute instance-attribute

RecognizedTargets

Bases: Enum

Source code in xdsl/backend/riscv/targets.py
267
268
269
270
271
272
class RecognizedTargets(Enum):
    riscv32_riscemu = TargetDefinition(MAbi.ILP32.value, MachineArchSpec("RV32IMA_Zto"))
    riscv64_linux = TargetDefinition(MAbi.LP64D.value, MachineArchSpec("RV64G"))
    snitch = TargetDefinition(
        MAbi.ILP32D.value, MachineArchSpec("RV32IMAD_Xssr_Xfrep_Xdma")
    )

riscv32_riscemu = TargetDefinition(MAbi.ILP32.value, MachineArchSpec('RV32IMA_Zto')) class-attribute instance-attribute

riscv64_linux = TargetDefinition(MAbi.LP64D.value, MachineArchSpec('RV64G')) class-attribute instance-attribute

snitch = TargetDefinition(MAbi.ILP32D.value, MachineArchSpec('RV32IMAD_Xssr_Xfrep_Xdma')) class-attribute instance-attribute