Questions on adding a CLANG builtin to connect to a LLVM intrinsic

Hello,

I have added new instructions to the RISC-V LLVM backend for some
specific hardware I developed and I want to access a sequence of them
directly from C code. As of now I do this using inline assembly but it
can be a bit cumbersome. If I'm not mistaken the way to go would be to
access this functionality from C with CLANG builtins and lowering this
to machine code with LLVM intrinsics that generate my target RISC-V
instructions.

I have seen the documentation on how to add LLVM intrinsics. However,
I can't find something similar with CLANG builtins, so any pointers to
some documentation would be greatly appreciated. Also, if there is a
better way of achieving this I would be grateful for some information
on how it could be done.

Best regards,
David

Just git grep for your favourite RISC-V builtin and copy it? You should
find two relevant files, ignoring tests.

Jess

This commit is a good example for how to add an intrinsic for risc-v:

https://github.com/llvm/llvm-project/commit/16877c5d2cd3f5c45642c9dc546c376ac87aa54d#diff-d340caed3f9af6c4e7ca78d4473ba5375a4973dc2f2b261644248ac039c0b2d1

Hello,

Following up on this, and trying to do something similar to
https://github.com/llvm/llvm-project/commit/16877c5, I am trying to
add a simple Clang builtin: void __builtin_riscv_foo() that should
output an intrinsic @llvm.riscv.foo() which will outpu, with a pattern
in the backend, a simple custom instruction FOO without parameters.
However, when compiling I receive the following error:

clang: llvm/lib/IR/Function.cpp:844: std::string
llvm::Intrinsic::getName(llvm::Intrinsic::ID,
llvm::ArrayRef<llvm::Type*>, llvm::Module*, llvm::FunctionType*):
Assertion `(Tys.empty() || Intrinsic::isOverloaded(Id)) && "This
version of getName is for overloaded intrinsics only"' failed.
PLEASE submit a bug report to https://bugs.llvm.org/ and include the
crash backtrace, preprocessed source, and associated run script.
Stack dump:
0. Program arguments: clang --target=riscv64 -march=rv64gcxbar
intrinsics_test.c -c -o intrinsics_test.o
1. <eof> parser at end of file
2. intrinsics_test.c:21:6: LLVM IR generation of declaration 'test'
3. intrinsics_test.c:21:6: Generating code for declaration 'test'
4. intrinsics_test.c:22:33: LLVM IR generation of compound statement ('{}')
5. intrinsics_test.c:23:37: LLVM IR generation of compound statement ('{}')

To get to this point I have added the following code:
In clang/include/clang/Basic/BuiltinsRISCV.def:
    TARGET_BUILTIN(__builtin_riscv_foo, "v", "n", "xbar")

In clang/lib/CodeGen/CGBuiltin.cpp : EmitRISCVBuiltinExpr:
    case RISCV::BI__builtin_riscv_foo: {
and later in that same function
    case RISCV::BI__builtin_riscv_foo:
        ID = Intrinsic::riscv_foo;
        break;

In llvm/include/llvm/IR/IntrinsicsRISCV.td:
    let TargetPrefix = "riscv" in {
        def int_riscv_foo : Intrinsic<, , >;
    }

In llvm/lib/Target/RISCV/RISCVISelLowering.h:
    FOO, // Before ISD::FIRST_TARGET_MEMORY_OPCODE

In llvm/lib/Target/RISCV/RISCVISelLowering.cpp : LowerINTRINSIC_WO_CHAIN:
    case Intrinsic::riscv_foo: {
        return DAG.getNode(RISCVISD::FOO, DL, XLenVT);
    }
and in RISCVTargetLowering::getTargetNodeName:
   NODE_NAME_CASE(FOO)

In llvm/lib/Target/RISCV/RISCVInstrInfoBar.td
    def riscv_foo : SDNode<"RISCVISD::FOO", SDTypeProfile<0, 0, >>;
    def : Pat<(riscv_foo), (FOO)>;

I think the test for the Clang builtin should be:
// RUN: %clang_cc1 -triple riscv64 -target-feature +xbar -emit-llvm %s -o - \
// RUN: | FileCheck %s -check-prefix=RV64Xbar

// RV64Xbar-LABEL: @foo(
// RV64Xbar-NEXT: entry:
// RV64Xbar-NEXT: call void @llvm.riscv.foo()
// RV64Xbar-NEXT: ret void
//
void foo() {
    __builtin_riscv_foo();
}

Is there something missing that I can't find? I have tried small
variations of this code with no success.

Best regards,
David

Hello,

Following up on this, and trying to do something similar to
https://github.com/llvm/llvm-project/commit/16877c5, I am trying to
add a simple Clang builtin: void __builtin_riscv_foo() that should
output an intrinsic @llvm.riscv.foo() which will outpu, with a pattern
in the backend, a simple custom instruction FOO without parameters.
However, when compiling I receive the following error:

clang: llvm/lib/IR/Function.cpp:844: std::string
llvm::Intrinsic::getName(llvm::Intrinsic::ID,
llvm::ArrayRef<llvm::Type*>, llvm::Module*, llvm::FunctionType*):
Assertion `(Tys.empty() || Intrinsic::isOverloaded(Id)) && "This
version of getName is for overloaded intrinsics only"' failed.

As you can see, it’s telling you that you’re treating your intrinsic as
an overloaded intrinsic, but your intrinsic isn’t polymorphic (it
doesn’t even have any arguments or return types, let alone overloaded).
You can see towards the bottom of EmitRISCVBuiltinExpr the
IntrinsicTypes = {ResultType}, which is the list of types used to
instantiate the overloads. Since you have no overloads, that list
should be empty for your intrinsic, so you should not be reusing that
bit of code and instead have a separate top-level case statement.

Jess

I see another issue that you’ll hit later. Your intrinsic doesn’t have IntrNoMem and has no results. So it will be in SelectionDAG as INTRINSIC_VOID rather that INTRINSIC_WO_CHAIN. Your getNode call will need to copy the chain input and output. And riscv_foo needs the SDNPHasChain property.

Thank you for your answers Jess and Craig.
Now the Clang builtin seems to be working and, as Craig predicted, I
got a new error "fatal error: error in backend: Cannot select:
intrinsic %llvm.riscv.foo" that I will try to debug with your
comments.

Best regards,
David