From 7078246f1ad4da24d346165233f53783fd8cf36c Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Fri, 16 May 2025 00:21:02 +0200 Subject: [PATCH] [CHERIoT] Add `cheriot_mmio`, `cheriot_shared_object` and `cheriot_cap_import` attributes The first two are used from the front-end to indicate that an extern global declaration refers to a specific compartment import. The third is the attribute consumed by the back-end to generate the necessary compartment import entry and the correct sequence of instructions when taking the address of the global. --- clang/include/clang/Basic/Attr.td | 13 + clang/include/clang/Basic/AttrDocs.td | 67 ++++ .../clang/Basic/DiagnosticSemaKinds.td | 10 + clang/lib/CodeGen/Targets/RISCV.cpp | 29 +- clang/lib/Sema/SemaDecl.cpp | 20 + clang/lib/Sema/SemaDeclAttr.cpp | 130 +++++++ .../cheri/riscv/cheriot-cap-import-attr.c | 190 ++++++++++ ...a-attribute-supported-attributes-list.test | 2 + clang/test/Misc/warning-flags.c | 4 +- .../cheriot-cap-import-attr-ill-formed.c | 14 + llvm/include/llvm/IR/Attributes.h | 256 +++++++++++++ llvm/include/llvm/IR/Cheri.h | 2 +- llvm/lib/IR/Verifier.cpp | 15 + llvm/lib/Target/RISCV/RISCV.h | 42 ++- llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp | 74 +++- .../Target/RISCV/RISCVExpandPseudoInsts.cpp | 110 ++++-- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 13 +- llvm/lib/Target/RISCV/RISCVTargetMachine.cpp | 4 +- llvm/lib/Target/RISCV/RISCVTargetMachine.h | 4 +- .../RISCV/cheri/cheri-local-libcall.ll | 4 +- .../CodeGen/RISCV/cheri/cheri-mcu-ccall.ll | 2 +- .../RISCV/cheri/cheri-mcu-ccallback.ll | 4 +- .../cheri/global-cap-import-attributes.ll | 356 ++++++++++++++++++ 23 files changed, 1300 insertions(+), 65 deletions(-) create mode 100644 clang/test/CodeGen/cheri/riscv/cheriot-cap-import-attr.c create mode 100644 clang/test/Sema/cheri/cheriot-cap-import-attr-ill-formed.c create mode 100644 llvm/test/CodeGen/RISCV/cheri/global-cap-import-attributes.ll diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 87ab30e2d7dab..23ccaf9a8761b 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2892,6 +2892,19 @@ def CHERILibCall : DeclOrTypeAttr { let Subjects = SubjectList<[Function], ErrorDiag>; } +def CHERIOTMMIODevice : DeclOrTypeAttr { + let Spellings = [GNU<"cheriot_mmio">]; + let Documentation = [CHERIOTMMIODeviceDocs]; + let Args = [StringArgument<"DeviceName">, StringArgument<"EncodedPermissions", 1>]; + let Subjects = SubjectList<[GlobalVar], ErrorDiag>; +} + +def CHERIOTSharedObject : DeclOrTypeAttr { + let Spellings = [GNU<"cheriot_shared_object">]; + let Documentation = [CHERIOTSharedObjectDocs]; + let Args = [StringArgument<"ObjectName">, StringArgument<"EncodedPermissions", 1>]; + let Subjects = SubjectList<[GlobalVar], ErrorDiag>; +} def CHERINoSubobjectBounds : DeclOrTypeAttr { let Spellings = [GNU<"cheri_no_subobject_bounds">, diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index a8b588169725a..7743815a15252 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1473,6 +1473,73 @@ Further reading for other annotations: }]; } +def CHERIOTMMIODeviceDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +Indicates that the global it refers to must be treated as a cross-compartment +object. In particular, this attribute indicates that the object is related to +an MMIO-bound device. The target global must have the `extern` and `volatile` +qualifiers. + +**Usage**: ``__attribute((cheriot_mmio("", +"")))``, where ``""`` is the (quoted) identifier +of the MMIO-bound device. An example of this is ``"uart"``. The +``""`` indicates the permissions of the capability +resulting from the import. The permissions encoding is a string of variable +length. The symbols allowed in the string are ``R``, ``W``, ``c`` and ``m``, +whose meaning is explained in the table below. + + +.. csv-table:: Supported Syntaxes + :header: "Symbol", "Description", "Meaning", "Depends on" + + "``R``","Load (Read)", "May be used to read.","" + "``W``","Store (Write)", "May be used to write.","" + "``c``","Load / Store Capability", "May be used to load or store capabilities as well as non-capability data.","``R`` or ``W``" + "``m``","Load Mutable", "May be used to load capabilities with write permission.","``R``" + +Examples of valid encodings are: ``"RWcm"`` (all permissions), ``"R"`` (read +only), etc. Note that the order in which symbols appear is not relevant: for +example, `"RWcm"` and `"mcWR"` are both valid and entail the same permissions. + +**Warning**: The ```` parameter is optional, and if no encoding is given ``"RWcm"`` is assumed. + }]; +} + + +def CHERIOTSharedObjectDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +Indicates that the global it refers to must be treated as a cross-compartment +object. In particular, this attribute indicates that the object is a +cross-compartment shared object. The target global must have the `extern` and +`volatile` qualifiers. + + +**Usage**: ``__attribute((cheriot_shared_object("", +"")))``, where ``""`` is the (quoted) identifier +of the shared object. The ``""`` indicates the permissions +of the capability resulting from the import. The permissions encoding is a +string of variable length. The symbols allowed in the string are ``R``, ``W``, +``c`` and ``m``, whose meaning is explained in the table below. + + +.. csv-table:: Supported Syntaxes + :header: "Symbol", "Description", "Meaning", "Depends on" + + "``R``","Load (Read)", "May be used to read.","" + "``W``","Store (Write)", "May be used to write.","" + "``c``","Load / Store Capability", "May be used to load or store capabilities as well as non-capability data.","``R`` or ``W``" + "``m``","Load Mutable", "May be used to load capabilities with write permission.","``R``" + +Examples of valid encodings are: ``"RWcm"`` (all permissions), ``"R"`` (read +only), etc. Note that the order in which symbols appear is not relevant: for +example, `"RWcm"` and `"mcWR"` are both valid and entail the same permissions. + +**Warning**: The ```` parameter is optional, and if no encoding is given ``"RWcm"`` is assumed. + }]; +} + def ObjCMethodFamilyDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b1e3e227b6c7c..67176bcef48f2 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -140,6 +140,16 @@ def err_half_const_requires_fp16 : Error< "half precision constant requires cl_khr_fp16">; def err_cheriot_minstack_without_annotation : Error< "__cheriot_minimum_stack__ can only be used in a function with a minimum stack annotation">; +def err_cheriot_malformed_cap_permissions_unknown_sym : Error< + "the permissions in %0 contain unknown permission symbols: '%1' (value: '%2') ">; +def err_cheriot_malformed_cap_permissions_invalid_dep : Error< + "the permissions in %0 contain ill-formed dependencies: %1 (value: '%2') ">; +def warn_cheriot_cap_permissions_duplicate_sym : Warning< + "the permissions in %0 contain a duplicate permission symbol: %1 (value: '%2') ">; +def warn_cheriot_global_cap_import_non_volatile : Warning< + "global variable definition '%0' has attribute %1 but is not qualified as `volatile`">; +def err_cheriot_global_cap_import_initialized: Error< + "global variable definition '%0' with attribute %1 cannot have an initializer">; // C99 variable-length arrays def ext_vla : Extension<"variable length arrays are a C99 feature">, diff --git a/clang/lib/CodeGen/Targets/RISCV.cpp b/clang/lib/CodeGen/Targets/RISCV.cpp index c5d9946f6a409..9e14fd312e656 100644 --- a/clang/lib/CodeGen/Targets/RISCV.cpp +++ b/clang/lib/CodeGen/Targets/RISCV.cpp @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "ABIInfoImpl.h" -#include "TargetInfo.h" #include "CommonCheriTargetCodeGenInfo.h" +#include "TargetInfo.h" using namespace clang; using namespace clang::CodeGen; @@ -632,6 +632,33 @@ class RISCVTargetCodeGenInfo : public CommonCheriTargetCodeGenInfo { void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &CGM) const override { + if (const auto *VD = dyn_cast_or_null(D)) { + if (auto *GVar = llvm::dyn_cast(GV)) { + + // If the clang declaration was annotated with `cheriot_mmio(...)`, we + // need to propagate the attribute by translating it to a matching + // `cheriot_cap_import` attribute in the generated LLVM IR, so that the + // back-end can generate the proper import entries and correctly + // translate references to this global. + if (VD->hasAttr()) { + auto *Attr = D->getAttr(); + llvm::CHERIoTGlobalCapabilityImportAttr CapImportAttr( + llvm::CHERIoTGlobalCapabilityImportAttr::MMIO, + Attr->getDeviceName(), Attr->getEncodedPermissions()); + GVar->addAttribute(CapImportAttr.getAttrName(), CapImportAttr.str()); + } + + // Same for the `cheriot_shared_object(...)` attribute. + if (VD->hasAttr()) { + auto *Attr = D->getAttr(); + llvm::CHERIoTGlobalCapabilityImportAttr CapImportAttr( + llvm::CHERIoTGlobalCapabilityImportAttr::SharedObject, + Attr->getObjectName(), Attr->getEncodedPermissions()); + GVar->addAttribute(CapImportAttr.getAttrName(), CapImportAttr.str()); + } + } + } + const auto *FD = dyn_cast_or_null(D); if (!FD) return; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index dc23cafa91426..dcb9deb38dd71 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13501,6 +13501,26 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { return; } + // CHERIoT-specific check: if the decl has the `cheriot_mmio` or + // `cheriot_shared_object` attributes and also has an explicit definition, an + // error must be generated. + if (RealDecl->hasAttr() || + RealDecl->hasAttr()) { + const IdentifierInfo *AttrName; + + if (auto *Attr = RealDecl->getAttr()) + AttrName = Attr->getAttrName(); + else { + AttrName = RealDecl->getAttr()->getAttrName(); + } + + Diag(RealDecl->getLocation(), + diag::err_cheriot_global_cap_import_initialized) + << VDecl->getName() << AttrName; + VDecl->setInvalidDecl(); + return; + } + // WebAssembly tables can't be used to initialise a variable. if (!Init->getType().isNull() && Init->getType()->isWebAssemblyTableType()) { Diag(Init->getExprLoc(), diag::err_wasm_table_art) << 0; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9572f1f3f441b..f3ece815e08fe 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -28,8 +28,10 @@ #include "clang/Basic/DarwinSDKInfo.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/Linkage.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Attr.h" @@ -61,6 +63,7 @@ #include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/IR/Attributes.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/MC/MCSectionMachO.h" #include "llvm/Support/Error.h" @@ -2103,6 +2106,127 @@ static void handleCHERIMethodSuffix(Sema &S, Decl *D, const ParsedAttr &Attr) { D->addAttr(::new (S.Context) CHERIMethodSuffixAttr(S.Context, Attr, Str)); } +static void handleCHERIOTMMIODevice(Sema &S, Decl *D, const ParsedAttr &Attr, + Sema::DeclAttributeLocation DAL) { + auto *VD = dyn_cast(D); + if (!VD || !VD->hasGlobalStorage()) + return; + + if (!VD->hasExternalStorage()) { + VD->setStorageClass(clang::SC_Extern); + } + + QualType QT = VD->getType(); + if (!QT.isVolatileQualified()) { + S.Diag(Attr.getLoc(), diag::warn_cheriot_global_cap_import_non_volatile) + << VD->getName() << Attr.getAttrName(); + } + + StringRef DeviceName; + SourceLocation DeviceNameLiteralLoc; + if (!S.checkStringLiteralArgumentAttr(Attr, 0, DeviceName, + &DeviceNameLiteralLoc)) + return; + StringRef Permissions; + SourceLocation PermissionsLiteralLoc; + + if (Attr.getNumArgs() > 1) { + S.checkStringLiteralArgumentAttr(Attr, 1, Permissions, + &PermissionsLiteralLoc); + } + + std::string OwnedPermissions = Permissions.str(); + + auto DuplicateSymbolCallback = [&Attr, &S, + Permissions](char DuplicateSymbol) { + S.Diag(Attr.getLoc(), diag::warn_cheriot_cap_permissions_duplicate_sym) + << Attr.getAttrName() << "'" + std::string({DuplicateSymbol}) + "'" + << Permissions; + }; + + auto ExtraneousSymbolCallback = [&Attr, &S, + Permissions](StringRef ExtraneousSymbols) { + S.Diag(Attr.getLoc(), + diag::err_cheriot_malformed_cap_permissions_unknown_sym) + << Attr.getAttrName() << ExtraneousSymbols << Permissions; + }; + + auto FailedSemanticCheckCallback = [&Attr, &S, + Permissions](StringRef Reason) { + S.Diag(Attr.getLoc(), + diag::err_cheriot_malformed_cap_permissions_invalid_dep) + << Attr.getAttrName() << Reason << Permissions; + }; + + if (!llvm::CHERIoTGlobalCapabilityImportAttr::checkPermissions( + OwnedPermissions, DuplicateSymbolCallback, ExtraneousSymbolCallback, + FailedSemanticCheckCallback)) + return; + + return D->addAttr(::new (S.Context) CHERIOTMMIODeviceAttr( + S.Context, Attr, DeviceName, OwnedPermissions)); +} + +static void handleCHERIOTSharedObject(Sema &S, Decl *D, const ParsedAttr &Attr, + Sema::DeclAttributeLocation DAL) { + auto *VD = dyn_cast(D); + if (!VD || !VD->hasGlobalStorage()) + return; + + if (VD->hasInit()) { + S.Diag(Attr.getLoc(), diag::err_cheriot_global_cap_import_initialized) + << VD->getName() << Attr.getAttrName(); + } + + if (!VD->hasExternalStorage()) { + VD->setStorageClass(clang::SC_Extern); + } + + StringRef ObjectName; + SourceLocation ObjectNameLiteralLoc; + if (!S.checkStringLiteralArgumentAttr(Attr, 0, ObjectName, + &ObjectNameLiteralLoc)) + return; + StringRef Permissions; + SourceLocation PermissionsLiteralLoc; + + if (Attr.getNumArgs() > 1) { + S.checkStringLiteralArgumentAttr(Attr, 1, Permissions, + &PermissionsLiteralLoc); + } + + std::string OwnedPermissions = Permissions.str(); + + auto DuplicateSymbolCallback = [&Attr, &S, + Permissions](char DuplicateSymbol) { + S.Diag(Attr.getLoc(), diag::warn_cheriot_cap_permissions_duplicate_sym) + << Attr.getAttrName() << "'" + std::string({DuplicateSymbol}) + "'" + << Permissions; + }; + + auto ExtraneousSymbolCallback = [&Attr, &S, + Permissions](StringRef ExtraneousSymbols) { + S.Diag(Attr.getLoc(), + diag::err_cheriot_malformed_cap_permissions_unknown_sym) + << Attr.getAttrName() << ExtraneousSymbols << Permissions; + }; + + auto FailedSemanticCheckCallback = [&Attr, &S, + Permissions](StringRef Reason) { + S.Diag(Attr.getLoc(), + diag::err_cheriot_malformed_cap_permissions_invalid_dep) + << Attr.getAttrName() << Reason << Permissions; + }; + + if (!llvm::CHERIoTGlobalCapabilityImportAttr::checkPermissions( + OwnedPermissions, DuplicateSymbolCallback, ExtraneousSymbolCallback, + FailedSemanticCheckCallback)) + return; + + D->addAttr(::new (S.Context) CHERIOTSharedObjectAttr( + S.Context, Attr, ObjectName, OwnedPermissions)); +} + static void handleCHERICompartmentName(Sema &S, Decl *D, const ParsedAttr &Attr, Sema::DeclAttributeLocation DAL) { // cheri_compartment is both: @@ -7350,6 +7474,12 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_CHERICompartmentName: handleCHERICompartmentName(S, D, AL, DAL); break; + case ParsedAttr::AT_CHERIOTMMIODevice: + handleCHERIOTMMIODevice(S, D, AL, DAL); + break; + case ParsedAttr::AT_CHERIOTSharedObject: + handleCHERIOTSharedObject(S, D, AL, DAL); + break; case ParsedAttr::AT_InterruptState: handleInterruptState(S, D, AL); break; diff --git a/clang/test/CodeGen/cheri/riscv/cheriot-cap-import-attr.c b/clang/test/CodeGen/cheri/riscv/cheriot-cap-import-attr.c new file mode 100644 index 0000000000000..eee0d6075feca --- /dev/null +++ b/clang/test/CodeGen/cheri/riscv/cheriot-cap-import-attr.c @@ -0,0 +1,190 @@ +// RUN: %clang_cc1 %s -o - "-triple" "riscv32cheriot-unknown-unknown" "-emit-llvm" "-mframe-pointer=none" "-mcmodel=small" "-target-abi" "cheriot" "-Oz" "-Werror" -std=c2x | FileCheck %s + +struct Uart {}; + +// No specific perm encoding means "RWcm". +// CHECK: @uart = external addrspace(200) global %struct.Uart, align 1 #0 +__attribute__((cheriot_mmio("uart"))) extern volatile struct Uart uart; + +// CHECK: @uart1 = external addrspace(200) global %struct.Uart, align 1 #0 +__attribute__((cheriot_mmio("uart", "WcmR"))) volatile struct Uart uart1; + +// CHECK: @uart2 = external addrspace(200) global %struct.Uart, align 1 #0 +__attribute__((cheriot_mmio("uart", "cmRW"))) volatile struct Uart uart2; + +// CHECK: @uart3 = external addrspace(200) global %struct.Uart, align 1 #0 +__attribute__((cheriot_mmio("uart", "RmcW"))) volatile struct Uart uart3; + +// CHECK: @uart4 = external addrspace(200) global %struct.Uart, align 1 #0 +__attribute__((cheriot_mmio("uart", "RmcW"))) volatile struct Uart uart4; + +// CHECK: @SO = external addrspace(200) global i32, align 4 #1 +__attribute__((cheriot_shared_object("SO"))) int SO; + +// CHECK: @SO1 = external addrspace(200) global i32, align 4 #1 +__attribute__((cheriot_shared_object("SO", "RWcm"))) extern int SO1; + +// CHECK: @SO2 = external addrspace(200) global i32, align 4 #1 +__attribute__((cheriot_shared_object("SO", "RWmc"))) extern int SO2; + +// CHECK: @SO3 = external addrspace(200) global i32, align 4 #1 +__attribute__((cheriot_shared_object("SO", "RmcW"))) extern int SO3; + +// CHECK: @SO4 = external addrspace(200) global i32, align 4 #1 +__attribute__((cheriot_shared_object("SO", "mRcW"))) extern int SO4; + +// CHECK: @uart5 = external addrspace(200) global %struct.Uart, align 1 #2 +__attribute__((cheriot_mmio("uart", "R"))) extern volatile struct Uart uart5; + +// CHECK: @uart6 = external addrspace(200) global %struct.Uart, align 1 #3 +__attribute__((cheriot_mmio("uart", "Rc"))) extern volatile struct Uart uart6; + +// CHECK: @uart61 = external addrspace(200) global %struct.Uart, align 1 #3 +__attribute__((cheriot_mmio("uart", "cR"))) extern volatile struct Uart uart61; + +// CHECK: @uart7 = external addrspace(200) global %struct.Uart, align 1 #4 +__attribute__((cheriot_mmio("uart", "Rcm"))) extern volatile struct Uart uart7; + +// CHECK: @uart71 = external addrspace(200) global %struct.Uart, align 1 #4 +__attribute__((cheriot_mmio("uart", "cRm"))) extern volatile struct Uart uart71; + +// CHECK: @uart72 = external addrspace(200) global %struct.Uart, align 1 #4 +__attribute__((cheriot_mmio("uart", "cmR"))) extern volatile struct Uart uart72; + +// CHECK: @uart73 = external addrspace(200) global %struct.Uart, align 1 #4 +__attribute__((cheriot_mmio("uart", "mRc"))) extern volatile struct Uart uart73; + +// CHECK: @uart8 = external addrspace(200) global %struct.Uart, align 1 #5 +__attribute__((cheriot_mmio("uart", "W"))) extern volatile struct Uart uart8; + +// CHECK: @uart9 = external addrspace(200) global %struct.Uart, align 1 #6 +__attribute__((cheriot_mmio("uart", "Wc"))) extern volatile struct Uart uart9; + +// CHECK: @uart91 = external addrspace(200) global %struct.Uart, align 1 #6 +__attribute__((cheriot_mmio("uart", "cW"))) extern volatile struct Uart uart91; + +// CHECK: @uart10 = external addrspace(200) global %struct.Uart, align 1 #7 +__attribute__((cheriot_mmio("uart", "RWc"))) extern volatile struct Uart uart10; + +// CHECK: @uart101 = external addrspace(200) global %struct.Uart, align 1 #7 +__attribute__((cheriot_mmio("uart", "cRW"))) extern volatile struct Uart uart101; + +// CHECK: @uart102 = external addrspace(200) global %struct.Uart, align 1 #7 +__attribute__((cheriot_mmio("uart", "RcW"))) extern volatile struct Uart uart102; + +// CHECK: @uart103 = external addrspace(200) global %struct.Uart, align 1 #7 +__attribute__((cheriot_mmio("uart", "WRc"))) extern volatile struct Uart uart103; + +// CHECK: @uart104 = external addrspace(200) global %struct.Uart, align 1 #7 +__attribute__((cheriot_mmio("uart", "WcR"))) extern volatile struct Uart uart104; + +// CHECK: @SO5 = external addrspace(200) global i32, align 4 #8 +__attribute__((cheriot_shared_object("SO", "R"))) extern int SO5; + +// CHECK: @SO6 = external addrspace(200) global i32, align 4 #9 +__attribute__((cheriot_shared_object("SO", "Rc"))) extern int SO6; + +// CHECK: @SO61 = external addrspace(200) global i32, align 4 #9 +__attribute__((cheriot_shared_object("SO", "cR"))) extern int SO61; + +// CHECK: @SO7 = external addrspace(200) global i32, align 4 #10 +__attribute__((cheriot_shared_object("SO", "Rcm"))) extern int SO7; + +// CHECK: @SO71 = external addrspace(200) global i32, align 4 #10 +__attribute__((cheriot_shared_object("SO", "cRm"))) extern int SO71; + +// CHECK: @SO72 = external addrspace(200) global i32, align 4 #10 +__attribute__((cheriot_shared_object("SO", "cmR"))) extern int SO72; + +// CHECK: @SO73 = external addrspace(200) global i32, align 4 #10 +__attribute__((cheriot_shared_object("SO", "mRc"))) extern int SO73; + +// CHECK: @SO8 = external addrspace(200) global i32, align 4 #11 +__attribute__((cheriot_shared_object("SO", "W"))) extern int SO8; + +// CHECK: @SO9 = external addrspace(200) global i32, align 4 #12 +__attribute__((cheriot_shared_object("SO", "Wc"))) extern int SO9; + +// CHECK: @SO91 = external addrspace(200) global i32, align 4 #12 +__attribute__((cheriot_shared_object("SO", "cW"))) extern int SO91; + +// CHECK: @SO10 = external addrspace(200) global i32, align 4 #13 +__attribute__((cheriot_shared_object("SO", "RWc"))) extern int SO10; + +// CHECK: @SO101 = external addrspace(200) global i32, align 4 #13 +__attribute__((cheriot_shared_object("SO", "cRW"))) extern int SO101; + +// CHECK: @SO102 = external addrspace(200) global i32, align 4 #13 +__attribute__((cheriot_shared_object("SO", "RcW"))) extern int SO102; + +// CHECK: @SO103 = external addrspace(200) global i32, align 4 #13 +__attribute__((cheriot_shared_object("SO", "WRc"))) extern int SO103; + +// CHECK: @SO104 = external addrspace(200) global i32, align 4 #13 +__attribute__((cheriot_shared_object("SO", "WcR"))) extern int SO104; + +void doSomethingWithUart(volatile struct Uart *uart); +void doSomethingWithSO(int *SO); + +void func() { + doSomethingWithUart(&uart); + doSomethingWithUart(&uart1); + doSomethingWithUart(&uart2); + doSomethingWithUart(&uart3); + doSomethingWithUart(&uart4); + + doSomethingWithSO(&SO); + doSomethingWithSO(&SO1); + doSomethingWithSO(&SO2); + doSomethingWithSO(&SO3); + doSomethingWithSO(&SO4); + + doSomethingWithUart(&uart5); + doSomethingWithUart(&uart6); + doSomethingWithUart(&uart61); + doSomethingWithUart(&uart7); + doSomethingWithUart(&uart71); + doSomethingWithUart(&uart72); + doSomethingWithUart(&uart73); + doSomethingWithUart(&uart8); + doSomethingWithUart(&uart9); + doSomethingWithUart(&uart91); + doSomethingWithUart(&uart10); + doSomethingWithUart(&uart101); + doSomethingWithUart(&uart102); + doSomethingWithUart(&uart103); + doSomethingWithUart(&uart104); + + doSomethingWithSO(&SO5); + doSomethingWithSO(&SO6); + doSomethingWithSO(&SO61); + doSomethingWithSO(&SO7); + doSomethingWithSO(&SO71); + doSomethingWithSO(&SO72); + doSomethingWithSO(&SO73); + doSomethingWithSO(&SO8); + doSomethingWithSO(&SO9); + doSomethingWithSO(&SO91); + doSomethingWithSO(&SO10); + doSomethingWithSO(&SO101); + doSomethingWithSO(&SO102); + doSomethingWithSO(&SO103); + doSomethingWithSO(&SO104); + +} + + +// CHECK: attributes #0 = { "cheriot_global_cap_import"="mem,uart,RWcm" } +// CHECK: attributes #1 = { "cheriot_global_cap_import"="cheriot_shared_object,SO,RWcm" } +// CHECK: attributes #2 = { "cheriot_global_cap_import"="mem,uart,R" } +// CHECK: attributes #3 = { "cheriot_global_cap_import"="mem,uart,Rc" } +// CHECK: attributes #4 = { "cheriot_global_cap_import"="mem,uart,Rcm" } +// CHECK: attributes #5 = { "cheriot_global_cap_import"="mem,uart,W" } +// CHECK: attributes #6 = { "cheriot_global_cap_import"="mem,uart,Wc" } +// CHECK: attributes #7 = { "cheriot_global_cap_import"="mem,uart,RWc" } +// CHECK: attributes #8 = { "cheriot_global_cap_import"="cheriot_shared_object,SO,R" } +// CHECK: attributes #9 = { "cheriot_global_cap_import"="cheriot_shared_object,SO,Rc" } +// CHECK: attributes #10 = { "cheriot_global_cap_import"="cheriot_shared_object,SO,Rcm" } +// CHECK: attributes #11 = { "cheriot_global_cap_import"="cheriot_shared_object,SO,W" } +// CHECK: attributes #12 = { "cheriot_global_cap_import"="cheriot_shared_object,SO,Wc" } +// CHECK: attributes #13 = { "cheriot_global_cap_import"="cheriot_shared_object,SO,RWc" } diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 69c163276f9c0..6ec7bc0909bf2 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -35,6 +35,8 @@ // CHECK-NEXT: CHERICCallee (SubjectMatchRule_function) // CHECK-NEXT: CHERICompartmentName (SubjectMatchRule_function) // CHECK-NEXT: CHERILibCall (SubjectMatchRule_function) +// CHECK-NEXT: CHERIOTMMIODevice (SubjectMatchRule_variable_is_global) +// CHECK-NEXT: CHERIOTSharedObject (SubjectMatchRule_variable_is_global) // CHECK-NEXT: CHERISubobjectBoundsUseRemainingSize (SubjectMatchRule_field, SubjectMatchRule_record) // CHECK-NEXT: CPUDispatch (SubjectMatchRule_function) // CHECK-NEXT: CPUSpecific (SubjectMatchRule_function) diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c index 1fd0244083335..6d0e386931930 100644 --- a/clang/test/Misc/warning-flags.c +++ b/clang/test/Misc/warning-flags.c @@ -18,7 +18,7 @@ This test serves two purposes: The list of warnings below should NEVER grow. It should gradually shrink to 0. -CHECK: Warnings without flags (61): +CHECK: Warnings without flags (63): CHECK-NEXT: ext_expected_semi_decl_list CHECK-NEXT: ext_missing_whitespace_after_macro_name @@ -38,6 +38,8 @@ CHECK-NEXT: warn_c_kext CHECK-NEXT: warn_call_wrong_number_of_arguments CHECK-NEXT: warn_case_empty_range CHECK-NEXT: warn_char_constant_too_large +CHECK-NEXT: warn_cheriot_cap_permissions_duplicate_sym +CHECK-NEXT: warn_cheriot_global_cap_import_non_volatile CHECK-NEXT: warn_collection_expr_type CHECK-NEXT: warn_conflicting_variadic CHECK-NEXT: warn_delete_array_type diff --git a/clang/test/Sema/cheri/cheriot-cap-import-attr-ill-formed.c b/clang/test/Sema/cheri/cheriot-cap-import-attr-ill-formed.c new file mode 100644 index 0000000000000..140216ac4f101 --- /dev/null +++ b/clang/test/Sema/cheri/cheriot-cap-import-attr-ill-formed.c @@ -0,0 +1,14 @@ +// RUN: %riscv32_cheri_cc1 "-triple" "riscv32cheriot-unknown-unknown" "-target-abi" "cheriot" -verify %s +struct Uart {}; + +__attribute__((cheriot_mmio("uart", "xyz"))) extern struct Uart uart; // expected-error{{the permissions in 'cheriot_mmio' contain unknown permission symbols: 'xyz' (value: 'xyz')}} expected-warning{{global variable definition 'uart' has attribute 'cheriot_mmio' but is not qualified as `volatile`}} +__attribute__((cheriot_mmio("uart", "RR"))) extern volatile struct Uart uart1; // expected-warning{{the permissions in 'cheriot_mmio' contain a duplicate permission symbol: 'R' (value: 'RR')}} +__attribute__((cheriot_mmio("uart", "m"))) extern volatile struct Uart uart2; // expected-error{{the permissions in 'cheriot_mmio' contain ill-formed dependencies: does not contain either read (R) or write (W) (value: 'm')}} +__attribute__((cheriot_mmio("uart", "R"))) volatile struct Uart uart3; // no warnings or errors, extern is implied +__attribute__((cheriot_mmio("uart"))) volatile struct Uart uart4 = {}; // expected-error{{global variable definition 'uart4' with attribute 'cheriot_mmio' cannot have an initializer}} +__attribute__((cheriot_mmio("uart"))) volatile struct {int k;} uart5 = {10}; // expected-error{{global variable definition 'uart5' with attribute 'cheriot_mmio' cannot have an initializer}} +__attribute__((cheriot_shared_object("exampleK", "RR"))) extern int exampleK; // expected-warning{{the permissions in 'cheriot_shared_object' contain a duplicate permission symbol: 'R' (value: 'RR')}} +__attribute__((cheriot_shared_object("exampleK", "m"))) extern int exampleK; // expected-error{{the permissions in 'cheriot_shared_object' contain ill-formed dependencies: does not contain either read (R) or write (W) (value: 'm')}} +__attribute__((cheriot_shared_object("exampleK", "Wm"))) extern int exampleK; // expected-error{{the permissions in 'cheriot_shared_object' contain ill-formed dependencies: contains mut (m) but does not have both read (R) and cap (c) (value: 'Wm')}} +__attribute__((cheriot_shared_object("exampleK", "R"))) int exampleK; // no warnings or errors, extern is implied +__attribute__((cheriot_shared_object("exampleK", "R"))) int exampleK2 = 10; // expected-error{{global variable definition 'exampleK2' with attribute 'cheriot_shared_object' cannot have an initializer}} diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h index 7612e553fe32e..c960988db58b4 100644 --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -1348,6 +1348,262 @@ void updateMinLegalVectorWidthAttr(Function &Fn, uint64_t Width); } // end namespace AttributeFuncs +/// Represents the LLVM-level attribute that is used to signal +/// cross-compartment imports of global (non-function) objects +class CHERIoTGlobalCapabilityImportAttr { +public: + /// The kind of object being imported. + enum ImportKind { + /// An MMIO-bound device. + MMIO, + /// A generic shared object. + SharedObject + }; + + /// Parses the string value previously constructed into an instance of the + /// attribute object. The constructed string must have the following + /// structure: `,,`. + /// + /// `` is used to uniquely identify the `ImportKind` of this + /// global capability import. The valid values are `MmioImportKindStr` and + /// `SharedObjectImportKindStr`. Using an unrecognizable value for + /// `` triggers a `llvm_unreachable`. + /// + /// The `` is the name of the imported object. `` is + /// assumed to be a well-formed and normalized permission string: this means + /// that it is assumed to be in this form: + /// + /// ``` + /// Permissions := ReadPermission WritePermissions CapPermission + /// MutPermission ReadPermission := `ReadPermissionSymbol` | "-" + /// WritePermission := `WritePermissionSymbol` | "-" + /// CapPermission := `CapPermissionSymbol` | "-" + /// WritePermission := `WritePermissionSymbol` | "-" + /// ``` + /// + /// The resulting `GlobalCapabilityImportAttr` will have the `ImportKind` and + /// `Domain` fields derived from ``, `ObjectName` equal to + /// `` and `Permissions` equal to ``. + CHERIoTGlobalCapabilityImportAttr(StringRef ValueRef) { + size_t NextSubstrStartsAt = 0; + + auto ImportKindStr = ValueRef.substr( + NextSubstrStartsAt, ValueRef.find(Separator) - NextSubstrStartsAt); + + if (ImportKindStr == MmioImportKindStr) { + ImportKind = ImportKind::MMIO; + Domain = MmioImportKindStr; + } else if (ImportKindStr == SharedObjectImportKindStr) { + ImportKind = ImportKind::SharedObject; + Domain = SharedObjectImportKindStr; + } else { + llvm_unreachable( + std::string("unknown import kind: " + ImportKindStr.str()).c_str()); + } + + NextSubstrStartsAt += ImportKindStr.size() + 1; + + if (NextSubstrStartsAt < ValueRef.size()) { + ObjectName = ValueRef.substr( + NextSubstrStartsAt, + ValueRef.find(Separator, NextSubstrStartsAt) - NextSubstrStartsAt); + } else { + ObjectName = StringRef(); + } + + NextSubstrStartsAt += ObjectName.size() + 1; + if (NextSubstrStartsAt < ValueRef.size()) { + Permissions = ValueRef.substr(NextSubstrStartsAt); + } else { + Permissions = StringRef(); + } + } + + CHERIoTGlobalCapabilityImportAttr(ImportKind ImportKind, StringRef ObjectName, + StringRef EncodedPermissions) + : ImportKind(ImportKind), ObjectName(ObjectName), + Permissions(EncodedPermissions) { + switch (ImportKind) { + case MMIO: + Domain = MmioImportKindStr; + break; + case SharedObject: + Domain = SharedObjectImportKindStr; + break; + } + }; + + /// Returns the name of attribute. + static const StringRef getAttrName() { + return StringRef("cheriot_global_cap_import"); + } + + /// Returns the string value for this CapabilityImportAttr. + std::string str() { + std::string ImportKindStr = ""; + switch (ImportKind) { + + case MMIO: + ImportKindStr = MmioImportKindStr; + break; + case SharedObject: + ImportKindStr = SharedObjectImportKindStr; + break; + } + return (ImportKindStr + Separator + ObjectName.str() + Separator + + Permissions.str()); + } + + /// Returns the list of valid permission symbols. + static auto getValidPermissionSymbols() { return &ValidSymbols; } + + /// Converts the permissions into an integer. + int64_t encodePermissions() { + return (((Permissions[0] == *ReadPermissionSymbol) ? (1 << 31) : 0) + + ((Permissions[1] == *WritePermissionSymbol) ? (1 << 30) : 0) + + ((Permissions[2] == *CapPermissionSymbol) ? (1 << 29) : 0) + + ((Permissions[3] == *MutPermissionSymbol) ? (1 << 28) : 0)); + } + + /// Checks whether a permission encoding respects the semantic constraints. + /// + /// This function assumes that the encoding is syntactically correct, that is, + /// it contains no extraneous symbols. + /// + /// If successful, this function modifies in-place the given string ref to a + /// known format. + static bool semanticCheckPermissions( + std::string &Permissions, + std::function FailedSemanticCheckCallback) { + + auto PermissionsRef = StringRef(Permissions); + auto HasRead = PermissionsRef.contains(ReadPermissionSymbol); + auto HasWrite = PermissionsRef.contains(WritePermissionSymbol); + auto HasMut = PermissionsRef.contains(MutPermissionSymbol); + auto HasCap = PermissionsRef.contains(CapPermissionSymbol); + + if (!HasRead && !HasWrite) { + FailedSemanticCheckCallback( + "does not contain either read (" + std::string(ReadPermissionSymbol) + + ") or write (" + std::string(WritePermissionSymbol) + ")"); + return false; + } + + if (HasMut && (!HasRead || !HasCap)) { + FailedSemanticCheckCallback( + "contains mut (" + std::string(MutPermissionSymbol) + + ") but does not have both read (" + + std::string(ReadPermissionSymbol) + ") and cap (" + + std::string(CapPermissionSymbol) + ")"); + return false; + } + + Permissions = (HasRead ? std::string(ReadPermissionSymbol) : "") + + (HasWrite ? std::string(WritePermissionSymbol) : "") + + (HasCap ? std::string(CapPermissionSymbol) : "") + + (HasMut ? std::string(MutPermissionSymbol) : ""); + return true; + } + + /// Checks whether a permission encoding respects the syntactic constraints. + static bool syntaxCheckPermissions( + std::string &Permissions, + std::function DuplicateSymbolCallback, + std::function ExtraneousSymbolCallback) { + + const auto ValidSymbolsSize = ValidSymbols.size(); + std::vector SymbolsCounter(ValidSymbolsSize, false); + std::string ExtraneousSymbols(""); + const auto *ValidSymbolsBegin = std::begin(ValidSymbols); + bool Duplicate; + + for (char C : Permissions) { + Duplicate = false; + + const unsigned long Pos = + std::find(ValidSymbolsBegin, ValidSymbolsBegin + ValidSymbolsSize, + C) - + ValidSymbolsBegin; + + if (Pos < ValidSymbolsSize) { + Duplicate = SymbolsCounter[Pos]; + SymbolsCounter[Pos] = true; + } else { + ExtraneousSymbols.push_back(C); + } + + if (Duplicate) + DuplicateSymbolCallback(C); + } + + if (!ExtraneousSymbols.empty()) { + ExtraneousSymbolCallback(ExtraneousSymbols); + return false; + } + + return true; + } + + static bool checkPermissions( + std::string &Permissions, + std::function DuplicateSymbolCallback = [](char) {}, + std::function ExtraneousSymbolCallback = + [](StringRef) {}, + std::function FailedSemanticCheckCallback = + [](std::string) {}) { + + // If no permissions were set, use the default ones. + if (Permissions.empty()) { + Permissions = defaultPermissions(); + } + + // Perform the syntactic checks. + if (!syntaxCheckPermissions(Permissions, DuplicateSymbolCallback, + ExtraneousSymbolCallback)) { + return false; + } + + // Semantics checks. + return semanticCheckPermissions(Permissions, FailedSemanticCheckCallback); + } + + /// Returns the default permissions. + static const std::string defaultPermissions() { + return std::string(ReadPermissionSymbol) + + std::string(WritePermissionSymbol) + + std::string(CapPermissionSymbol) + std::string(MutPermissionSymbol); + } + + ImportKind ImportKind; + StringRef Domain; + StringRef ObjectName; + StringRef Permissions; + +private: + /// The separator used when creating the final string value for the + /// attribute. + static constexpr const char *Separator = ","; + + /// The symbol for the read permission. + static constexpr const char *ReadPermissionSymbol = "R"; + + /// The symbol for the write permission. + static constexpr const char *WritePermissionSymbol = "W"; + + /// The symbol for the cap permission. + static constexpr const char *CapPermissionSymbol = "c"; + + /// The symbol for the mut permission. + static constexpr const char *MutPermissionSymbol = "m"; + + static constexpr const std::array ValidSymbols = { + ReadPermissionSymbol[0], WritePermissionSymbol[0], CapPermissionSymbol[0], + MutPermissionSymbol[0]}; + + static constexpr const char *MmioImportKindStr = "mem"; + static constexpr const char *SharedObjectImportKindStr = + "cheriot_shared_object"; +}; } // end namespace llvm #endif // LLVM_IR_ATTRIBUTES_H diff --git a/llvm/include/llvm/IR/Cheri.h b/llvm/include/llvm/IR/Cheri.h index fb5b44dc450ff..b86bee38d0655 100644 --- a/llvm/include/llvm/IR/Cheri.h +++ b/llvm/include/llvm/IR/Cheri.h @@ -61,7 +61,7 @@ bool isKnownUntaggedCapability(const Value* V, const DataLayout* DL); // User-permissions are shifted by 15 in CAndPerm/CGetPerm constexpr unsigned MIPS_UPERMS_SHIFT = 15; -} +} // namespace cheri #if 0 /// Same again but try to derive the DataLayout from an llvm::Module diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 577cc674b1fcb..0c84366de08ca 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -812,6 +812,21 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) { Check(!GV.hasSection(), "tagged GlobalValue must not be in section.", &GV); } + /// CHERIoT-specific check. If the global value is: + /// 1. a global variable + /// 2. has the "global capability import" attribute + /// then it must be a reference an external object. + const auto CheriAttrName = + llvm::CHERIoTGlobalCapabilityImportAttr::getAttrName(); + auto *GVar = dyn_cast(&GV); + if (GVar && GVar->hasAttribute(CheriAttrName)) { + Check((GV.isDeclaration() && + (GV.hasExternalLinkage() || GV.hasExternalWeakLinkage())) || + GV.hasAvailableExternallyLinkage(), + "Global has the " + CheriAttrName + " attribute, but is not external", + &GV); + } + forEachUser(&GV, GlobalValueVisited, [&](const Value *V) -> bool { if (const Instruction *I = dyn_cast(V)) { if (!I->getParent() || !I->getParent()->getParent()) diff --git a/llvm/lib/Target/RISCV/RISCV.h b/llvm/lib/Target/RISCV/RISCV.h index ee6281107c36d..3f17ac0ba1d1a 100644 --- a/llvm/lib/Target/RISCV/RISCV.h +++ b/llvm/lib/Target/RISCV/RISCV.h @@ -45,41 +45,49 @@ FunctionPass *createRISCVLandingPadSetupPass(); void initializeRISCVLandingPadSetupPass(PassRegistry &); /// Information about imported functions. -struct CHERIoTImportedFunction { +struct CHERIoTImportedObject { /// The name of the import symbol. - StringRef ImportName; - /// The name of the import symbol. - StringRef ExportName; + std::string ImportName; + /// The name of the export symbol. + std::string ExportName; + /// The name of the used symbol. + std::string Name; /// Flag indicating whether this is a library or compartment import. bool IsLibrary; /// Flag indicating that the entry should be public and a COMDAT. bool IsPublic; + /// Flag indicating that the entry is a global symbol. + bool IsGlobal; + /// May hold, if the import needs it, the value of the encoded permissions + /// that will be used when computing the value of the second word of the + /// generated entry. + std::optional MaybeSecondWordPermissionsEncoding; }; /** - * Helper class to allow CHERIoTImportedFunction structures to be used in a + * Helper class to allow CHERIoTImportedObject structures to be used in a * dense map. */ -struct CHERIoTImportedFunctionDenseMapInfo { +struct CHERIoTImportedObjectDenseMapInfo { /// Anything with an empty string is invalid, use a canonical zero value. - static CHERIoTImportedFunction getEmptyKey() { - return {"", "", false, false}; + static CHERIoTImportedObject getEmptyKey() { + return {"", "", "", false, false, false, std::nullopt}; } /// Anything with an empty string is invalid, use the IsPublic field to /// differentiate from the canonical zero value. - static CHERIoTImportedFunction getTombstoneKey() { - return {"", "", true, false}; + static CHERIoTImportedObject getTombstoneKey() { + return {"", "", "", true, false, false, std::nullopt}; } /// The import name is unique within a compilation unit, use it for the hash. - static unsigned getHashValue(const CHERIoTImportedFunction &Val) { + static unsigned getHashValue(const CHERIoTImportedObject &Val) { return llvm::hash_value(Val.ImportName); } /// Compare for equality. - static bool isEqual(const CHERIoTImportedFunction &LHS, - const CHERIoTImportedFunction &RHS) { + static bool isEqual(const CHERIoTImportedObject &LHS, + const CHERIoTImportedObject &RHS) { // Don't bother comparing export names. It's an error to have two imports // with mismatched export names (two different imports referring to the // same export may be permitted). Similarly, IsPublic depends on the @@ -90,9 +98,9 @@ struct CHERIoTImportedFunctionDenseMapInfo { }; /// The set of functions imported from this compilation unit. -using CHERIoTImportedFunctionSet = SetVector< - CHERIoTImportedFunction, std::vector, - DenseSet>; +using CHERIoTImportedObjectSet = SetVector< + CHERIoTImportedObject, std::vector, + DenseSet>; FunctionPass *createRISCVISelDag(RISCVTargetMachine &TM, CodeGenOptLevel OptLevel); @@ -112,7 +120,7 @@ void initializeRISCVOptWInstrsPass(PassRegistry &); FunctionPass *createRISCVMergeBaseOffsetOptPass(); void initializeRISCVMergeBaseOffsetOptPass(PassRegistry &); -FunctionPass *createRISCVExpandPseudoPass(CHERIoTImportedFunctionSet &); +FunctionPass *createRISCVExpandPseudoPass(CHERIoTImportedObjectSet &); void initializeRISCVExpandPseudoPass(PassRegistry &); FunctionPass *createRISCVPreRAExpandPseudoPass(); diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp index 10b2c190c3569..46cbd0d2dc691 100644 --- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp +++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -30,6 +30,7 @@ #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Module.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" @@ -40,7 +41,6 @@ #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" -#include "llvm/MC/MCSectionELF.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/RISCVISAInfo.h" @@ -137,6 +137,7 @@ class RISCVAsmPrinter : public AsmPrinter { void emitAttributes(const MCSubtargetInfo &SubtargetInfo); void emitNTLHint(const MachineInstr *MI); + void emitGlobalVariable(const GlobalVariable *GV) override; // XRay Support void LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr *MI); @@ -290,6 +291,28 @@ bool RISCVAsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst, // instructions) auto-generated. #include "RISCVGenMCPseudoLowering.inc" +void RISCVAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { + auto CheriotCapImportAttrName = + llvm::CHERIoTGlobalCapabilityImportAttr::getAttrName(); + + if (GV->hasAttribute(CheriotCapImportAttrName)) { + // This global variable refers to an object exported from a different + // compartment. + // + // Nothing has to be emitted here, in this case. The import entry is + // properly registered when the variable that has this attribute is used. + + assert(!GV->isLocalLinkage(GV->getLinkage()) && + "Global is defined locally, which is not allowed " + "for globals marked with the global_cap_import attribute."); + + return; + } + + // Continue through the normal path to emit the global. + return AsmPrinter::emitGlobalVariable(GV); +} + // If the target supports Zihintntl and the instruction has a nontemporal // MachineMemOperand, emit an NTLH hint instruction before it. void RISCVAsmPrinter::emitNTLHint(const MachineInstr *MI) { @@ -693,7 +716,7 @@ void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) { } // Generate CHERIoT imports if there are any. auto &CHERIoTCompartmentImports = - static_cast(TM).ImportedFunctions; + static_cast(TM).ImportedObjects; if (!CHERIoTCompartmentImports.empty()) { auto &C = OutStreamer->getContext(); @@ -704,19 +727,27 @@ void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) { // Public symbols must be COMDATs so that they can be merged across // compilation units. Private ones must not be. - auto *Section = - Entry.IsPublic - ? C.getELFSection(".compartment_imports", ELF::SHT_PROGBITS, - ELF::SHF_ALLOC | ELF::SHF_GROUP, 0, - Entry.ImportName, true) - : C.getELFSection(".compartment_imports", ELF::SHT_PROGBITS, - ELF::SHF_ALLOC); + MCSectionELF *Section; + + if (Entry.IsPublic) { + Section = C.getELFSection(".compartment_imports." + Entry.Name, + ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_GROUP | + (Entry.IsGlobal ? ELF::SHF_WRITE : 0), + 0, Entry.ImportName, true); + } else { + Section = C.getELFSection(".compartment_imports." + Entry.Name, + ELF::SHT_PROGBITS, ELF::SHF_ALLOC); + } + OutStreamer->switchSection(Section); - auto Sym = C.getOrCreateSymbol(Entry.ImportName); - auto ExportSym = C.getOrCreateSymbol(Entry.ExportName); + auto *Sym = C.getOrCreateSymbol(Entry.ImportName); + auto *ExportSym = C.getOrCreateSymbol(Entry.ExportName); OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeObject); - if (Entry.IsPublic) + if (Entry.IsPublic && !Entry.IsGlobal) OutStreamer->emitSymbolAttribute(Sym, MCSA_Weak); + if (Entry.IsGlobal) + OutStreamer->emitSymbolAttribute(Sym, llvm::MCSA_Global); OutStreamer->emitValueToAlignment(Align(8)); OutStreamer->emitLabel(Sym); // Library imports have their low bit set. @@ -727,7 +758,24 @@ void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) { 4); else OutStreamer->emitValue(MCSymbolRefExpr::create(ExportSym, C), 4); - OutStreamer->emitIntValue(0, 4); + if (!Entry.MaybeSecondWordPermissionsEncoding.has_value()) + OutStreamer->emitIntValue(0, 4); + else { + auto PermissionsEncoding = + Entry.MaybeSecondWordPermissionsEncoding.value(); + auto MangledExportNameEnd = Entry.ExportName + "_end"; + auto *EncodedPermissionsExpr = + MCConstantExpr::create(PermissionsEncoding, C); + auto *ExportSymRef = MCSymbolRefExpr::create(ExportSym, C); + auto *ExportSymEnd = C.getOrCreateSymbol(MangledExportNameEnd); + auto *ExportSymEndRef = MCSymbolRefExpr::create(ExportSymEnd, C); + auto *ExportLength = + MCBinaryExpr::createSub(ExportSymEndRef, ExportSymRef, C); + auto *SecondWordValue = + MCBinaryExpr::createAdd(ExportLength, EncodedPermissionsExpr, C); + + OutStreamer->emitValue(SecondWordValue, 4); + } OutStreamer->emitELFSize(Sym, MCConstantExpr::create(8, C)); } } diff --git a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp index 6e992e7efde4d..a57699238bc7f 100644 --- a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp +++ b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp @@ -29,6 +29,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" +#include using namespace llvm; @@ -42,10 +43,10 @@ class RISCVExpandPseudo : public MachineFunctionPass { const RISCVSubtarget *STI; const RISCVInstrInfo *TII; static char ID; - CHERIoTImportedFunctionSet &ImportedFunctions; + CHERIoTImportedObjectSet &ImportedObjects; - RISCVExpandPseudo(CHERIoTImportedFunctionSet &CHERIoTImports) - : MachineFunctionPass(ID), ImportedFunctions(CHERIoTImports) { } + RISCVExpandPseudo(CHERIoTImportedObjectSet &CHERIoTImports) + : MachineFunctionPass(ID), ImportedObjects(CHERIoTImports) {} bool runOnMachineFunction(MachineFunction &MF) override; @@ -120,10 +121,12 @@ class RISCVExpandPseudo : public MachineFunctionPass { * * Calls the result if `CallImportTarget` is true. */ - MachineBasicBlock *insertLoadOfImportTable( - MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, - MCSymbol *ImportSymbol, MCSymbol *ExportSymbol, Register DestReg, - bool IsLibrary, bool IsPublic, bool CallImportTarget); + MachineBasicBlock * + insertLoadOfImportTable(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + MCSymbol *ImportSymbol, MCSymbol *ExportSymbol, + const StringRef ImportName, Register DestReg, + bool IsLibrary, bool IsPublic, bool CallImportTarget); #ifndef NDEBUG unsigned getInstSizeInBytes(const MachineFunction &MF) const { @@ -285,9 +288,9 @@ MachineBasicBlock *RISCVExpandPseudo::insertLoadOfImportTable( // it will remain valid for the duration of codegen. MCSymbol *ImportSymbol = MF->getContext().getOrCreateSymbol(ImportEntryName); MCSymbol *ExportSymbol = MF->getContext().getOrCreateSymbol(ExportEntryName); - return insertLoadOfImportTable(MBB, MBBI, ImportSymbol, ExportSymbol, DestReg, - IsLibrary || TreatAsLibrary, - Fn->hasExternalLinkage(), CallImportTarget); + return insertLoadOfImportTable( + MBB, MBBI, ImportSymbol, ExportSymbol, ImportName, DestReg, + IsLibrary || TreatAsLibrary, Fn->hasExternalLinkage(), CallImportTarget); } static const GlobalValue *resolveGlobalAlias(const GlobalValue *GV) { @@ -299,8 +302,8 @@ static const GlobalValue *resolveGlobalAlias(const GlobalValue *GV) { MachineBasicBlock *RISCVExpandPseudo::insertLoadOfImportTable( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, - MCSymbol *ImportSymbol, MCSymbol *ExportSymbol, Register DestReg, - bool IsLibrary, bool IsPublic, bool CallImportTarget) { + MCSymbol *ImportSymbol, MCSymbol *ExportSymbol, const StringRef ImportName, + Register DestReg, bool IsLibrary, bool IsPublic, bool CallImportTarget) { auto *MF = MBB.getParent(); const DebugLoc DL = MBBI->getDebugLoc(); MachineBasicBlock *NewMBB = @@ -329,8 +332,9 @@ MachineBasicBlock *RISCVExpandPseudo::insertLoadOfImportTable( // Make the original basic block fall-through to the new. MBB.addSuccessor(NewMBB); - ImportedFunctions.insert( - {ImportSymbol->getName(), ExportSymbol->getName(), IsLibrary, IsPublic}); + ImportedObjects.insert({ImportSymbol->getName().str(), + ExportSymbol->getName().str(), ImportName.str(), + IsLibrary, IsPublic, false, std::nullopt}); LivePhysRegs LiveRegs; computeAndAddLiveIns(LiveRegs, *NewMBB); return NewMBB; @@ -460,8 +464,9 @@ bool RISCVExpandPseudo::expandLibraryCall( MF->getContext().getOrCreateSymbol(ImportEntryName); MCSymbol *ExportSymbol = MF->getContext().getOrCreateSymbol(ExportEntryName); - insertLoadOfImportTable(MBB, MBBI, ImportSymbol, ExportSymbol, RISCV::C7, - true, true, true); + insertLoadOfImportTable(MBB, MBBI, ImportSymbol, ExportSymbol, + Callee.getSymbolName(), RISCV::C7, true, true, + true); NextMBBI = MBB.end(); } else { @@ -546,14 +551,67 @@ bool RISCVExpandPseudo::expandAuipccInstPair( MF->insert(++MBB.getIterator(), NewMBB); - BuildMI(NewMBB, DL, TII->get(RISCV::AUIPCC), TmpReg) - .addDisp(Symbol, 0, FlagsHi); + auto CheriotCapImportAttrName = + llvm::CHERIoTGlobalCapabilityImportAttr::getAttrName(); + std::optional CheriotCapImportAttr = std::nullopt; + + if (Symbol.isGlobal()) { + auto *GV = llvm::dyn_cast(Symbol.getGlobal()); + if (GV && GV->hasAttribute(CheriotCapImportAttrName)) { + CheriotCapImportAttr.emplace(GV->getAttribute(CheriotCapImportAttrName)); + } + } + + if (CheriotCapImportAttr.has_value()) { + MCContext &Ctxt = MF->getContext(); + llvm::CHERIoTGlobalCapabilityImportAttr CapAttr( + CheriotCapImportAttr->getValueAsString()); + + // The prefixed name of the import without permissions, e.g. `mem_uart`. + std::string PrefixedImportName = + (CapAttr.Domain + "_" + CapAttr.ObjectName).str(); + + // The prefixed name of the import with permissions, e.g. `mem_uart_RWcm`. + std::string PrefixedImportNameWithPermissions = + (PrefixedImportName + "_" + CapAttr.Permissions).str(); + + // The name of the import without permissions, e.g. `uart`. + auto ImportName = CapAttr.ObjectName.str(); + + // The permissions of the import, e.g. `RWcm`. + auto CapabilityPermissions = CapAttr.Permissions.str(); + + std::string ExportPrefix = ""; + + // The MMIO kind of imports needs an ad-hoc `export_` prefix. + if (CapAttr.ImportKind == llvm::CHERIoTGlobalCapabilityImportAttr::MMIO) { + ExportPrefix = "export_"; + } + + auto MangledExportName = ("__" + ExportPrefix + PrefixedImportName); + auto MangledExportNameEnd = MangledExportName + "_end"; + auto MangledImportName = "__import_" + PrefixedImportNameWithPermissions; + MCSymbol *MangledImportSymbol = Ctxt.getOrCreateSymbol(MangledImportName); + + BuildMI(NewMBB, DL, TII->get(RISCV::AUIPCC), TmpReg) + .addSym(MangledImportSymbol, FlagsHi); + + auto EncodedPermissions = CapAttr.encodePermissions(); + + ImportedObjects.insert({MangledImportName, MangledExportName, ImportName, + false, true, true, EncodedPermissions}); + } else { + BuildMI(NewMBB, DL, TII->get(RISCV::AUIPCC), TmpReg) + .addDisp(Symbol, 0, FlagsHi); + } + BuildMI(NewMBB, DL, TII->get(SecondOpcode), DestReg) .addReg(TmpReg) .addMBB(NewMBB, IsCheriot ? RISCVII::MO_CHERIOT_COMPARTMENT_LO_I : RISCVII::MO_PCREL_LO); - if (!InBounds && MF->getSubtarget().isRV32E() && - Symbol.isGlobal() && isa(Symbol.getGlobal()) && + if (!CheriotCapImportAttr.has_value() && !InBounds && + MF->getSubtarget().isRV32E() && Symbol.isGlobal() && + isa(Symbol.getGlobal()) && (cast(Symbol.getGlobal())->getSection() != ".compartment_imports")) BuildMI(NewMBB, DL, TII->get(RISCV::CSetBoundsImm), DestReg) @@ -590,6 +648,14 @@ bool RISCVExpandPseudo::expandCapLoadLocalCap( RISCV::CIncOffsetImm); const GlobalValue *GV = Symbol.getGlobal(); + auto *GVar = llvm::dyn_cast(GV); + if (GVar && GVar->hasAttribute( + llvm::CHERIoTGlobalCapabilityImportAttr::getAttrName())) { + return expandAuipccInstPair(MBB, MBBI, NextMBBI, + RISCVII::MO_CHERIOT_COMPARTMENT_HI, + RISCV::CLC_64); + } + if (!isa(GV) && !cast(GV)->isConstant()) return expandAuicgpInstPair(MBB, MBBI, NextMBBI, RISCV::CIncOffsetImm, InBounds); @@ -1130,12 +1196,12 @@ bool RISCVPreRAExpandPseudo::expandLoadTLSDescAddress( namespace llvm { template <> Pass *callDefaultCtor() { - static CHERIoTImportedFunctionSet CHERIoTImports; + static CHERIoTImportedObjectSet CHERIoTImports; return new RISCVExpandPseudo(CHERIoTImports); } FunctionPass * -createRISCVExpandPseudoPass(CHERIoTImportedFunctionSet &CHERIoTImports) { +createRISCVExpandPseudoPass(CHERIoTImportedObjectSet &CHERIoTImports) { return new RISCVExpandPseudo(CHERIoTImports); } diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index aaa3b971403c0..ae683e018411c 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -8232,6 +8232,18 @@ SDValue RISCVTargetLowering::lowerGlobalAddress(SDValue Op, // another function. Therefore, we always load from the captable for all // global variables. const GlobalValue *GV = N->getGlobal(); + + if (auto *GVar = llvm::dyn_cast(GV)) { + auto AttrName = llvm::CHERIoTGlobalCapabilityImportAttr::getAttrName(); + if (GVar->hasAttribute(AttrName)) { + SDLoc DL(N); + SDValue Addr = getTargetNode(N, DL, Ty, DAG, 0); + // Force it to be lowered to a `CLLC` regardless what `getAddr` would + // produce. + return DAG.getNode(RISCVISD::CLLC, DL, Ty, Addr); + } + } + return getAddr(N, Ty, DAG, GV->isDSOLocal(), /*CanDeriveFromPcc=*/false, GV->hasExternalWeakLinkage()); } @@ -20756,7 +20768,6 @@ SDValue RISCVTargetLowering::LowerFormalArguments( // the vector itself. VT = VA.getLocVT(); } - stackArgumentSize = std::max(stackArgumentSize, VA.getLocMemOffset() + VT.getStoreSize()); } diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp index 135dbbac2810e..761279eb19683 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -600,8 +600,8 @@ void RISCVPassConfig::addPreEmitPass2() { addPass(createRISCVPushPopOptimizationPass()); } addPass(createRISCVIndirectBranchTrackingPass()); - addPass(createRISCVExpandPseudoPass( - getTM().ImportedFunctions)); + addPass( + createRISCVExpandPseudoPass(getTM().ImportedObjects)); // Schedule the expansion of AMOs at the last possible moment, avoiding the // possibility for other passes to break the requirements for forward diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.h b/llvm/lib/Target/RISCV/RISCVTargetMachine.h index b5aece71d73cf..675fd2dc0a8a1 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetMachine.h +++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.h @@ -66,8 +66,8 @@ class RISCVTargetMachine : public CodeGenTargetMachineImpl { SMRange &SourceRange) const override; void registerPassBuilderCallbacks(PassBuilder &PB) override; - /// The set of functions imported from this compilation unit. - CHERIoTImportedFunctionSet ImportedFunctions; + /// The set of objects imported from this compilation unit. + CHERIoTImportedObjectSet ImportedObjects; }; std::unique_ptr diff --git a/llvm/test/CodeGen/RISCV/cheri/cheri-local-libcall.ll b/llvm/test/CodeGen/RISCV/cheri/cheri-local-libcall.ll index bd3bb5cc6f720..1840e1bb9ae5e 100644 --- a/llvm/test/CodeGen/RISCV/cheri/cheri-local-libcall.ll +++ b/llvm/test/CodeGen/RISCV/cheri/cheri-local-libcall.ll @@ -34,11 +34,11 @@ attributes #1 = { minsize mustprogress nofree norecurse nosync nounwind optsize ; CHECK-NEXT: .byte 0 ; CHECK-NEXT: .byte 8 ; CHECK-NEXT: .size __export_hello__Z9say_hellov, 4 -; CHECK-NEXT: .section .compartment_imports,"aG",@progbits,__library_import_hello__Z11id_functionv,comdat +; CHECK-NEXT: .section .compartment_imports._Z11id_functionv,"aG",@progbits,__library_import_hello__Z11id_functionv,comdat ; CHECK-NEXT: .type __library_import_hello__Z11id_functionv,@object ; CHECK-NEXT: .weak __library_import_hello__Z11id_functionv ; CHECK-NEXT: .p2align 3, 0x0 ; CHECK-NEXT:__library_import_hello__Z11id_functionv: ; CHECK-NEXT: .word __library_export_hello__Z11id_functionv+1 ; CHECK-NEXT: .word 0 -; CHECK-NEXT: .size __library_import_hello__Z11id_functionv, 8 \ No newline at end of file +; CHECK-NEXT: .size __library_import_hello__Z11id_functionv, 8 diff --git a/llvm/test/CodeGen/RISCV/cheri/cheri-mcu-ccall.ll b/llvm/test/CodeGen/RISCV/cheri/cheri-mcu-ccall.ll index 3511e96e9f205..65b8bc634fe88 100644 --- a/llvm/test/CodeGen/RISCV/cheri/cheri-mcu-ccall.ll +++ b/llvm/test/CodeGen/RISCV/cheri/cheri-mcu-ccall.ll @@ -157,7 +157,7 @@ attributes #6 = { minsize nounwind optsize } ; CHECK: .byte 0 ; CHECK: .byte 7 ; CHECK: .size __export_example_test8, 4 -; CHECK: .section .compartment_imports,"aG",@progbits,__import_other_test8callee,comdat +; CHECK: .section .compartment_imports.test8callee,"aG",@progbits,__import_other_test8callee,comdat ; CHECK: .type __import_other_test8callee,@object ; CHECK: .weak __import_other_test8callee ; CHECK: .p2align 3 diff --git a/llvm/test/CodeGen/RISCV/cheri/cheri-mcu-ccallback.ll b/llvm/test/CodeGen/RISCV/cheri/cheri-mcu-ccallback.ll index 5a3022001cb64..8d2215d6328d7 100644 --- a/llvm/test/CodeGen/RISCV/cheri/cheri-mcu-ccallback.ll +++ b/llvm/test/CodeGen/RISCV/cheri/cheri-mcu-ccallback.ll @@ -66,7 +66,7 @@ entry: } ; Make sure that the import and export table entries are local for the internal function. -; CHECK: .section .compartment_imports,"a",@progbits +; CHECK: .section .compartment_imports.cb,"a",@progbits ; CHECK-NOT: comdat ; CHECK-NOT: .globl __import_comp_cb ; CHECK: __import_comp_cb: @@ -74,7 +74,7 @@ entry: ; CHECK-NOT: .globl __export_comp_cb ; CHECK-NOT: .globl __export_comp_ecb ; Make sure that the import table entry a COMDAT for the external function -; CHECK: .section .compartment_imports,"aG",@progbits,__import_comp_ecb,comdat +; CHECK: .section .compartment_imports.ecb,"aG",@progbits,__import_comp_ecb,comdat ; CHECK: .type __import_comp_ecb,@object ; CHECK: .weak __import_comp_ecb ; CHECK: .p2align 3 diff --git a/llvm/test/CodeGen/RISCV/cheri/global-cap-import-attributes.ll b/llvm/test/CodeGen/RISCV/cheri/global-cap-import-attributes.ll new file mode 100644 index 0000000000000..416a43c9d2846 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/cheri/global-cap-import-attributes.ll @@ -0,0 +1,356 @@ +; RUN: llc --filetype=asm --mcpu=cheriot --mtriple=riscv32-unknown-unknown-cheriotrtos -target-abi cheriot -mattr=+xcheri,+cap-mode < %s | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-n32-S128-pf200:64:64:64:32-A200-P200-G200" +target triple = "riscv32cheriot-unknown-cheriotrtos" + +%struct.Uart = type { i8 } + + +; Function Attrs: mustprogress noinline optnone +define dso_local void @_Z7examplev() addrspace(200) #0 { +entry: +; CHECK: .LBB0_1: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_----") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_1)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_no_perm) +; CHECK: .LBB0_2: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_--c-") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_2)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_c) +; CHECK: .LBB0_3: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_---m") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_3)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_m) + +; CHECK: .LBB0_4: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_--cm") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_4)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_cm) + +; CHECK: .LBB0_5: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_-W--") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_5)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_W) + +; CHECK: .LBB0_6: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_-Wc-") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_6)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_Wc) + +; CHECK: .LBB0_7: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_-W-m") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_7)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_Wm) + +; CHECK: .LBB0_8: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_-Wcm") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_8)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_Wcm) + +; CHECK: .LBB0_9: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_R---") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_9)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_R) + +; CHECK: .LBB0_10: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_R-c-") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_10)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_Rc) + +; CHECK: .LBB0_11: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_R--m") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_11)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_Rm) + +; CHECK: .LBB0_12: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_R-cm") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_12)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_Rcm) + +; CHECK: .LBB0_13: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_RW--") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_13)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_RW) + +; CHECK: .LBB0_14: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_RWc-") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_14)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_RWc) + +; CHECK: .LBB0_15: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi("__import_mem_uart_RW-m") +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_15)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_RWm) + +;; We will add more calls to this global, so it will be automatically spilled +;; to the stack to preserve it across calls. + +; CHECK: .LBB0_16: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc cs1, %cheriot_compartment_hi(__import_mem_uart_RWcm) +; CHECK-NEXT: clc cs1, %cheriot_compartment_lo_i(.LBB0_16)(cs1) +; CHECK-NEXT: cmove ca0, cs1 +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_RWcm) + +; CHECK-NEXT: cmove ca0, cs1 +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_RWcm) + +; CHECK-NEXT: cmove ca0, cs1 +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_RWcm) + +; CHECK-NEXT: cmove ca0, cs1 +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_RWcm) + +; CHECK-NEXT: cmove ca0, cs1 +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_RWcm) + +; Duplicate entry should refer to `__import_mem_uart_RWcm`, too. + +; CHECK: .LBB0_17: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc cs1, %cheriot_compartment_hi(__import_mem_uart_RWcm) +; CHECK-NEXT: clc cs1, %cheriot_compartment_lo_i(.LBB0_17)(cs1) +; CHECK-NEXT: cmove ca0, cs1 +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_RWcm2) + +; CHECK-NEXT: cmove ca0, cs1 +; CHECK-NEXT: ccall _Z19doSomethingWithUartP4Uart + call void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef @uart_RWcm2) + +; CHECK: .LBB0_18: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca0, %cheriot_compartment_hi(__import_cheriot_shared_object_shared_obj_RWcm) +; CHECK-NEXT: clc ca0, %cheriot_compartment_lo_i(.LBB0_18)(ca0) +; CHECK-NEXT: ccall _Z19doSomethingWithSharedObjectP4SharedObject + call void @_Z19doSomethingWithSharedObjectP4SharedObject(ptr addrspace(200) noundef @shared_obj_RWcm) + + ret void +} + +declare dso_local void @_Z19doSomethingWithUartP4Uart(ptr addrspace(200) noundef) addrspace(200) #1 +declare dso_local void @_Z19doSomethingWithSharedObjectP4SharedObject(ptr addrspace(200) noundef) addrspace(200) #1 + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_----",comdat +; CHECK-NEXT: .type "__import_mem_uart_----",@object +; CHECK-NEXT: .globl "__import_mem_uart_----" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_----": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)+0 +; CHECK-NEXT: .size "__import_mem_uart_----", 8 +@uart_no_perm = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,----" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_--c-",comdat +; CHECK-NEXT: .type "__import_mem_uart_--c-",@object +; CHECK-NEXT: .globl "__import_mem_uart_--c-" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_--c-": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)+536870912 +; CHECK-NEXT: .size "__import_mem_uart_--c-", 8 +@uart_c = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,--c-" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_---m",comdat +; CHECK-NEXT: .type "__import_mem_uart_---m",@object +; CHECK-NEXT: .globl "__import_mem_uart_---m" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_---m": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)+268435456 +; CHECK-NEXT: .size "__import_mem_uart_---m", 8 +@uart_m = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,---m" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_--cm",comdat +; CHECK-NEXT: .type "__import_mem_uart_--cm",@object +; CHECK-NEXT: .globl "__import_mem_uart_--cm" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_--cm": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)+805306368 +; CHECK-NEXT: .size "__import_mem_uart_--cm", 8 +@uart_cm = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,--cm" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_-W--",comdat +; CHECK-NEXT: .type "__import_mem_uart_-W--",@object +; CHECK-NEXT: .globl "__import_mem_uart_-W--" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_-W--": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)+1073741824 +; CHECK-NEXT: .size "__import_mem_uart_-W--", 8 +@uart_W = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,-W--" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_-Wc-",comdat +; CHECK-NEXT: .type "__import_mem_uart_-Wc-",@object +; CHECK-NEXT: .globl "__import_mem_uart_-Wc-" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_-Wc-": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)+1610612736 +; CHECK-NEXT: .size "__import_mem_uart_-Wc-", 8 +@uart_Wc = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,-Wc-" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_-W-m",comdat +; CHECK-NEXT: .type "__import_mem_uart_-W-m",@object +; CHECK-NEXT: .globl "__import_mem_uart_-W-m" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_-W-m": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)+1342177280 +; CHECK-NEXT: .size "__import_mem_uart_-W-m", 8 +@uart_Wm = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,-W-m" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_-Wcm",comdat +; CHECK-NEXT: .type "__import_mem_uart_-Wcm",@object +; CHECK-NEXT: .globl "__import_mem_uart_-Wcm" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_-Wcm": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)+1879048192 +; CHECK-NEXT: .size "__import_mem_uart_-Wcm", 8 +@uart_Wcm = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,-Wcm" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_R---",comdat +; CHECK-NEXT: .type "__import_mem_uart_R---",@object +; CHECK-NEXT: .globl "__import_mem_uart_R---" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_R---": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)-2147483648 +; CHECK-NEXT: .size "__import_mem_uart_R---", 8 +@uart_R = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,R---" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_R-c-",comdat +; CHECK-NEXT: .type "__import_mem_uart_R-c-",@object +; CHECK-NEXT: .globl "__import_mem_uart_R-c-" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_R-c-": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)-1610612736 +; CHECK-NEXT: .size "__import_mem_uart_R-c-", 8 +@uart_Rc = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,R-c-" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_R--m",comdat +; CHECK-NEXT: .type "__import_mem_uart_R--m",@object +; CHECK-NEXT: .globl "__import_mem_uart_R--m" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_R--m": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)-1879048192 +; CHECK-NEXT: .size "__import_mem_uart_R--m", 8 +@uart_Rm = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,R--m" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_R-cm",comdat +; CHECK-NEXT: .type "__import_mem_uart_R-cm",@object +; CHECK-NEXT: .globl "__import_mem_uart_R-cm" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_R-cm": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)-1342177280 +; CHECK-NEXT: .size "__import_mem_uart_R-cm", 8 +@uart_Rcm = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,R-cm" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_RW--",comdat +; CHECK-NEXT: .type "__import_mem_uart_RW--",@object +; CHECK-NEXT: .globl "__import_mem_uart_RW--" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_RW--": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)-1073741824 +; CHECK-NEXT: .size "__import_mem_uart_RW--", 8 +@uart_RW = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,RW--" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_RWc-",comdat +; CHECK-NEXT: .type "__import_mem_uart_RWc-",@object +; CHECK-NEXT: .globl "__import_mem_uart_RWc-" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_RWc-": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)-536870912 +; CHECK-NEXT: .size "__import_mem_uart_RWc-", 8 +@uart_RWc = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,RWc-" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,"__import_mem_uart_RW-m",comdat +; CHECK-NEXT: .type "__import_mem_uart_RW-m",@object +; CHECK-NEXT: .globl "__import_mem_uart_RW-m" +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: "__import_mem_uart_RW-m": +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)-805306368 +; CHECK-NEXT: .size "__import_mem_uart_RW-m", 8 +@uart_RWm = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,RW-m" + +; Arbitrarily duplicated entry, made to check that two different globals that +; refer to the same mmio device do not produce two duplicated entries in the +; imports table. +@uart_RWcm2 = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,RWcm" + +; CHECK: .section .compartment_imports.uart,"awG",@progbits,__import_mem_uart_RWcm,comdat +; CHECK-NEXT: .type __import_mem_uart_RWcm,@object +; CHECK-NEXT: .globl __import_mem_uart_RWcm +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: __import_mem_uart_RWcm: +; CHECK-NEXT: .word __export_mem_uart +; CHECK-NEXT: .word (__export_mem_uart_end-__export_mem_uart)-268435456 +; CHECK-NEXT: .size __import_mem_uart_RWcm, 8 +@uart_RWcm = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "mem,uart,RWcm" + +; CHECK: .section .compartment_imports.shared_obj,"awG",@progbits,__import_cheriot_shared_object_shared_obj_RWcm,comdat +; CHECK-NEXT: .type __import_cheriot_shared_object_shared_obj_RWcm,@object +; CHECK-NEXT: .globl __import_cheriot_shared_object_shared_obj_RWcm +; CHECK-NEXT: .p2align 3, 0x0 +; CHECK-NEXT: __import_cheriot_shared_object_shared_obj_RWcm: +; CHECK-NEXT: .word __cheriot_shared_object_shared_obj +; CHECK-NEXT: .word (__cheriot_shared_object_shared_obj_end-__cheriot_shared_object_shared_obj)-268435456 +; CHECK-NEXT: .size __import_cheriot_shared_object_shared_obj_RWcm, 8 +@shared_obj_RWcm = external addrspace(200) global %struct.Uart, align 1 "cheriot_global_cap_import" = "cheriot_shared_object,shared_obj,RWcm" + +attributes #0 = { mustprogress noinline optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cheriot" "target-features"="+32bit,+c,+cap-mode,+e,+m,+relax,+xcheri,+zmmul,-a,-b,-d,-experimental-sdext,-experimental-sdtrig,-experimental-smctr,-experimental-ssctr,-experimental-svukte,-experimental-xqcia,-experimental-xqciac,-experimental-xqcicli,-experimental-xqcicm,-experimental-xqcics,-experimental-xqcicsr,-experimental-xqciint,-experimental-xqcilo,-experimental-xqcilsm,-experimental-xqcisls,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-experimental-zvbc32e,-experimental-zvkgs,-f,-h,-i,-sha,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smdbltrp,-smepmp,-smmpm,-smnpm,-smrnmi,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssdbltrp,-ssnpm,-sspm,-ssqosid,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-supm,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-svvptc,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xmipscmove,-xmipslsp,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zacas,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b" } +attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cheriot" "target-features"="+32bit,+c,+cap-mode,+e,+m,+relax,+xcheri,+zmmul,-a,-b,-d,-experimental-sdext,-experimental-sdtrig,-experimental-smctr,-experimental-ssctr,-experimental-svukte,-experimental-xqcia,-experimental-xqciac,-experimental-xqcicli,-experimental-xqcicm,-experimental-xqcics,-experimental-xqcicsr,-experimental-xqciint,-experimental-xqcilo,-experimental-xqcilsm,-experimental-xqcisls,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-experimental-zvbc32e,-experimental-zvkgs,-f,-h,-i,-sha,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smdbltrp,-smepmp,-smmpm,-smnpm,-smrnmi,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssdbltrp,-ssnpm,-sspm,-ssqosid,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-supm,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-svvptc,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xmipscmove,-xmipslsp,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zacas,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b" } + +!llvm.module.flags = !{!0, !1, !2, !4, !5} +!llvm.ident = !{!6} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 1, !"target-abi", !"cheriot"} +!2 = !{i32 6, !"riscv-isa", !3} +!3 = !{!"rv32e2p0_m2p0_c2p0_zmmul1p0_xcheri0p0"} +!4 = !{i32 7, !"frame-pointer", i32 2} +!5 = !{i32 8, !"SmallDataLimit", i32 0} +!6 = !{!"clang version 20.1.3 (https://github.com/CHERIoT-Platform/llvm-project.git 44c01c22f58a1fa95df120c6045886ca38c44339)"}