ASM Style Guide
Naming Convention
The name of a type, an attribute, or an operation should be in snake_case
. Examples:
!cir.int
!cir.long_double<!cir.float>
!cir.data_member
#cir.cmp3way_info
#cir.dyn_cast_info
cir.binop
cir.get_member
The name of a type parameter, an attribute parameter, or an operation argument and result, when defined in TableGen and printed in CIR assembly, should be in snake_case
.
Any keywords that appears in the syntax of a type, an attribute, or an operation, should be in snake_case
.
Assembly Syntax Convention
General Syntax Format
Each operation should follow the following general syntax:
cir.op <args> <regions> `:` <typing> <attr>
Where:
<args>
is the operation's arguments list.<regions>
is any regions introduced by the operation.<typing>
is the operation's type specifications.<attrs>
is any attributes attached to the operation.
Arguments List
The arguments list should include all the arguments (except for some attributes as specified in the "placement of attribute" section later) to the operation. Don't use parenthesis around the arguments list. The most common format of an arguments list is just a comma-separated list:
%3 = cir.libc.memchr %0, %1, %2 : (!cir.ptr<!cir.void>, !s32i, !u64i) -> !cir.ptr<!cir.void>
Beyond that, one can also use auxiliary keywords and some mini-syntaxes in the arguments list to make the operation's assembly more readable:
cir.store %0 to %1 : !s32i, !cir.ptr<!s32i>
%2 = cir.get_runtime_member %0[%1] : (!cir.ptr<!struct>, !cir.data_member<!s32i in !struct>) -> !cir.ptr<!s32i>
Regions
For operations that introduces regions, the regions should follow the arguments list in the assembly. One may use auxiliary keywords in the region list to make the assembly more readable:
cir.if %0 {
...
} else {
...
} : !cir.bool
cir.ternary %2 true {
...
cir.yield %0 : !s32i
} false {
...
cir.yield %1 : !s32i
} : !cir.bool -> !s32i
Type Specifications
The type specification gives the types of operation operands and results. Type specifications are placed after the regions list (if any), separated from it with a colon.
If the operation has trait SameOperandsAndResultType
or AllTypesMatch
, the type specification should only give a single type that represents the type of the operands and the results:
%1 = cir.cos %0 : !cir.float
%2 = cir.binop add %0, %1 : !s32i
Otherwise, the type specification can be further split into two type lists separated by a right arrow ( ->
). Before the arrow is a type list for the operands, and after the arrow is a type list for the results. Types in a type list are separated by commas; when a type list includes more than one type, surround the type list with a parenthesis. If at least one of the two type lists are empty, the empty type list(s) and the right arrow can be omitted.
If the operation has trait SameTypeOperands
, the type list for the operands should only has one type that give the type of the operands:
%2 = cir.ptr_diff %0, %1 : !cir.ptr<!s32i> -> !u64i
Otherwise, the type specification should include a type for each of the operands and the results. Examples:
%2 = cir.get_runtime_member %0[%1] : (!cir.ptr<!struct>, !cir.data_member<!s32i in !struct>) -> !cir.ptr<!s32i>
Placement of Attributes
Attributes on an operation can be classified into the following 2 categories:
- Required attributes. These attributes are required to build a valid operation. Missing of these attributes either makes the operation meaningless or breaks some passes in the CIR pipeline. Note that
UnitAttr
should be regarded as a boolean attribute for the purpose of classification. - Optional attributes. These attributes can always be safely removed from the operation, without breaking anything in CIR.
Required Attributes
Put required attributes in the argument list. One may place the attribute at any appropriate position within the argument list to make the assembly readable. Example:
%0 = cir.const #cir.int<42> : !s32i
%1 = cir.unary inc %0 : !s32i
%2 = cir.binop add %0, %1 : !s32i
If the attribute is too long in its assembly form (longer than just a keyword or an attribute name), one should define an alias for the attribute:
#dyn_cast_info_1 = #cir.dyn_cast_info<...>
#cmp3wayinfo = #cir.cmp3way_info<...>
%1 = cir.dyn_cast ptrcast %0 #dyn_cast_info_1 : !cir.ptr<!struct> -> !cir.ptr<!cir.void>
%2 = cir.cmp3way %0, %1 #cmp3wayinfo : !s32i -> !u8i
Optional Attributes
Put optional attributes in the <attrs>
part with the attr-dict
TableGen directive:
cir.global external @foo = #cir.int<42> : !s32i #cir.ast
Note that the above example categories a
VarDecl
AST reference to a global variable declaration node as an optional attribute. But not all AST reference attributes are "optional"; you should categorize each specific attribute according to the conventions listed before.
Current Status
The status quo doesn't implement this full guide just yet, but while we convert more code to use it, new entities added to the dialect shall follow this guide.