[llvm] [RISCV] AI Foundry ET extensions for RISC-V (PR #174571)
Abel Bernabeu via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 9 15:07:14 PST 2026
================
@@ -0,0 +1,789 @@
+//===-- RISCVInstrInfoXAIF.td ------------------------------*- tablegen -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Instruction definitions for the AI Foundry ET extensions, formerly known as
+// the ET-SoC-1 Minion extensions by Esperanto Technologies.
+//
+// Full documentation for these extensions is publicly available at the
+// following URL:
+//
+// https://github.com/aifoundry-org/et-man/blob/main/ET%20Programmer's%20Reference%20Manual.pdf
+//
+// The following conventions are used in this file:
+//
+// 1. Specific instruction formats are named
+//
+// RVInstET<#>
+//
+// where <#> is a number from 1 to 14. Each number corresponds to a template
+// fitting a given instruction and other similar ones, as defined in
+// RISCVInstrFormatsXAIF.td
+//
+// 2. Each masked vector instruction <INST> is handled with an ETOps<Operands>
+// multiclass defining one instruction and two pseudo instructions:
+//
+// - <INST>: regular masked vector instructions as described in the manual,
+// implicitly using M0 register for masking out destination register
+// elements.
+//
+// - <INST>_EX: masked vector pseudo-instruction explictly accepting M0 as
+// its last operand, as opposed to <INST> where the register dependency
+// is just implicit. In addition to the explicit operands accepted by
+// <INST>, we must add m0 as the last operand.
+//
+// The purpose of this instruction variant is to explicitly mark the
+// dependency on the m0 register contents in MIR (Machine Instruction
+// Representation).
+//
+// - <INST>_PASSTHRU_EX: Similar to <INST_EX>, but also making explicit the
+// dependency on the previous destination register contents. In addition
+// to the explicit operands accepted by <INST_EX>, we must add the
+// destination register as the first operand.
+//
+// Destination and first source operands are constrained to be assigned
+// the same physical register during register allocation.
+//
+// This variant should be the choice during instruction selection for
+// cases when m0 is not guaranteed to be all ones.
+//
+//===----------------------------------------------------------------------===//
+
+let DecoderNamespace = "XAIF" in {
+let Predicates = [HasXAIFET] in {
+
+ let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in
+ def FLQ2 : RVInstI<0b101, OPC_LOAD_FP,
+ (outs FPR256:$rd),
+ (ins simm12_lo:$imm12, GPR:$rs1),
+ "aif.flq2", "$rd, ${imm12}(${rs1})">;
+
+ let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in
+ def FSQ2 : RVInstS<0b101, OPC_STORE_FP,
+ (outs),
+ (ins FPR256:$rs2, simm12_lo:$imm12, GPR:$rs1),
+ "aif.fsq2", "$rs2, ${imm12}(${rs1})">;
+
+ let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in {
+ def FLW_PS : RVInstI<0b010, OPC_ET_MEM_PS,
+ (outs FPR256:$rd),
+ (ins simm12_lo:$imm12, GPR:$rs1),
+ "aif.flw.ps", "$rd, ${imm12}(${rs1})">;
+ def FLW_PS_EX : Pseudo<(outs FPR256:$rd),
+ (ins simm12_lo:$imm12, GPR:$rs1, MR0:$m0), [],
+ "aif.flw.ps", "$rd, ${imm12}(${rs1}), $m0">;
+ def FLW_PS_PASSTHRU_EX : Pseudo<(outs FPR256:$rd),
+ (ins FPR256:$in, simm12_lo:$imm12, GPR:$rs1, MR0:$m0), [],
+ "aif.flw.ps", "$rd, ${imm12}(${rs1}), $m0"> {
+ let Constraints = "$rd = $in";
+ }
+ }
+
+ let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in {
+ def FSW_PS : RVInstS<0b110, OPC_ET_MEM_PS,
+ (outs),
+ (ins FPR256:$rs2, simm12_lo:$imm12, GPR:$rs1),
+ "aif.fsw.ps", "$rs2, ${imm12}(${rs1})">;
+ def FSW_PS_EX : Pseudo<(outs),
+ (ins FPR256:$rs2, simm12_lo:$imm12, GPR:$rs1, MR0:$m0),
+ [], "aif.fsw.ps", "$rs2, ${imm12}(${rs1}), $m0">;
+ }
+
+ let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in {
+ def FBC_PS : RVInstI<0b000, OPC_ET_MEM_PS,
+ (outs FPR256:$rd),
+ (ins simm12_lo:$imm12, GPR:$rs1),
+ "aif.fbc.ps", "$rd, ${imm12}(${rs1})">;
+ def FBC_PS_EX : Pseudo<(outs FPR256:$rd),
+ (ins simm12_lo:$imm12, GPR:$rs1, MR0:$m0), [],
+ "aif.fbc.ps", "$rd, ${imm12}(${rs1}), $m0">;
+ }
+
+ let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
+ def FBCX_PS : RVInstI<0b011, OPC_ET_MEM_PS,
+ (outs FPR256:$rd),
+ (ins GPR:$rs1),
+ "aif.fbcx.ps", "$rd, ${rs1}"> {
+ let imm12 = 0;
+ }
+ def FBCX_PS_EX : Pseudo<(outs FPR256:$rd),
+ (ins GPR:$rs1, MR0:$m0),[],
+ "aif.fbcx.ps", "$rd, ${rs1}, $m0">;
+ def FBCX_PS_PASSTHRU_EX : Pseudo<(outs FPR256:$rd),
+ (ins FPR256:$in, GPR:$rs1, MR0:$m0),[],
+ "aif.fbcx.ps", "$rd, ${rs1}, $m0"> {
+ let Constraints = "$rd = $in";
+ }
+
+ def FBCI_PS : RVInstU<OPC_ET_IMM_PS,
+ (outs FPR256:$rd),
+ (ins uimm20_lui:$imm20),
+ "aif.fbci.ps", "$rd, $imm20"> ;
+
+ def FBCI_PS_EX : Pseudo<(outs FPR256:$rd),
+ (ins uimm20_lui:$imm20, MR0:$m0), [],
+ "aif.fbci.ps", "$rd, $imm20, $m0">;
+ def FBCI_PS_PASSTHRU_EX : Pseudo<(outs FPR256:$rd),
+ (ins FPR256:$in, uimm20_lui:$imm20, MR0:$m0), [],
+ "aif.fbci.ps", "$rd, $imm20, $m0"> {
+ let Constraints = "$rd = $in";
+ }
+
+ multiclass ETOpsRdF1R2G<bits<7> funct7, string opcodestr> {
+ def "" : RVInstR<funct7, 0b001, OPC_ET_MEM_PS,
+ (outs FPR256:$rd),
+ (ins FPR256:$rs1, GPRMemZeroOffset:$rs2),
+ opcodestr, "$rd, ${rs1}, ${rs2}">;
+ def _EX : Pseudo<(outs FPR256:$rd),
+ (ins FPR256:$rs1, GPRMemZeroOffset:$rs2, MR0:$m0), [],
+ opcodestr, "$rd, ${rs1}, ${rs2}, $m0">;
+ def _PASSTHRU_EX : Pseudo<(outs FPR256:$rd),
+ (ins FPR256:$in, FPR256:$rs1, GPRMemZeroOffset:$rs2, MR0:$m0), [],
+ opcodestr, "$rd, ${rs1}, ${rs2}, $m0"> {
+ let Constraints = "$rd = $in";
+ }
+ }
+
+ let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in {
----------------
abel-bernabeu wrote:
TLDR: double checked and this is fine.
The innermost `let` wins. The outer `let` is a default setting that only gets overriden as needed. This kind of `let` nesting is common in multiclasses.
If those properties were not well set the load/store ISel patterns (which I have on purpose not included here) would have been flagged as invalid on the debug builds. The full thing would fall apart with basic unit tests from our LLVM 11 branch.
Nevertheless, I have checked and the properties are well set.
`FGW_PS` and `FADD_PS` are in the same `let` scope. For those we have that:
- `FGW_PS` overrides with `hasSideEffects = 0, mayLoad = 1, mayStore = 0`
- `FADD_PS` uses defaults, and has `hasSideEffects = 0, mayLoad = 0, mayStore = 0`
Also did a simpler test with this:
`class MyClass {
int Value;
}
let Value = 0 in {
let Value = 1 in
def OVERRIDEN : MyClass;
def DEFAULT : MyClass;
}`
I see two records being produced:
```
def OVERRIDEN { // MyClass
int Value = 1;
}
def DEFAULT { // MyClass
int Value = 0;
}
```
The way `let` works is by maintaining a stack of default initializers for fields. Those initializers are applied whenever classes are instantiated. Opening new `let` scopes pushes new initializers.
The interesting part is is that you doubted, which means the idiom is not obvious.
https://github.com/llvm/llvm-project/pull/174571
More information about the llvm-commits
mailing list