diff --git a/clang/lib/CodeGen/Targets/RISCV.cpp b/clang/lib/CodeGen/Targets/RISCV.cpp index 32218989aefae..2b0e6e1cd848e 100644 --- a/clang/lib/CodeGen/Targets/RISCV.cpp +++ b/clang/lib/CodeGen/Targets/RISCV.cpp @@ -35,6 +35,36 @@ class RISCVABIInfo : public DefaultABIInfo { llvm::Type *&Field2Ty, CharUnits &Field2Off) const; + /// CHERI(oT)-specific: Figure out how many registers are needed to pass a + /// value of the given type and size directly in register(s). The value of + /// `-1` means that the value should be passed indirectly. + /// + /// Note: `Size` is in bits. + int8_t getCapRegsForStruct(uint64_t Size, QualType Ty) const { + auto &T = getTarget(); + auto CapWidth = T.getCHERICapabilityWidth(); + + if (Size % CapWidth) + return -1; + + auto *RT = Ty->getAs(); + + StringRef TargetABI = T.getABI(); + bool IsCheriot = TargetABI == "cheriot" || TargetABI == "cheriot-baremetal"; + + auto Q = Size / CapWidth; + + if (RT && getContext().containsCapabilities(RT->getDecl())) { + if (Q == 1) + return 1; + + if (IsCheriot && Q == 2) + return 2; + } + + return -1; + } + public: RISCVABIInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen, unsigned FLen, bool EABI) @@ -428,19 +458,16 @@ ABIArgInfo RISCVABIInfo::classifyArgumentType(QualType Ty, bool IsFixed, if (isEmptyRecord(getContext(), Ty, true) && Size == 0) return ABIArgInfo::getIgnore(); - bool IsSingleCapRecord = false; - if (auto *RT = Ty->getAs()) - IsSingleCapRecord = Size == getTarget().getCHERICapabilityWidth() && - getContext().containsCapabilities(RT->getDecl()); - - bool IsCapability = Ty->isCHERICapabilityType(getContext()) || - IsSingleCapRecord; + auto CapRegsForStruct = getCapRegsForStruct(Size, Ty); + bool ForcePassInCapRegs = CapRegsForStruct > 0; + bool IsSingleCapability = + Ty->isCHERICapabilityType(getContext()) || CapRegsForStruct == 1; // Capabilities (including single-capability records, which are treated the // same as a single capability) are passed indirectly for hybrid varargs. // Anything larger is bigger than 2*XLEN and thus automatically passed // indirectly anyway. - if (!IsFixed && IsCapability && + if (!IsFixed && IsSingleCapability && !getContext().getTargetInfo().areAllPointersCapabilities()) { if (ArgGPRsLeft) ArgGPRsLeft -= 1; @@ -495,9 +522,9 @@ ABIArgInfo RISCVABIInfo::classifyArgumentType(QualType Ty, bool IsFixed, // TODO: Pairs involving capabilities should be passed in registers too like // int/fp pairs (requires thought for fp+cap when out of FPRs). int NeededArgGPRs = 1; - if (!IsCapability && !IsFixed && NeededAlign == 2 * XLen) + if (!IsSingleCapability && !IsFixed && NeededAlign == 2 * XLen) NeededArgGPRs = 2 + (EABI && XLen == 32 ? 0 : (ArgGPRsLeft % 2)); - else if (!IsCapability && Size > XLen && Size <= 2 * XLen) + else if (!IsSingleCapability && Size > XLen && Size <= 2 * XLen) NeededArgGPRs = 2; if (NeededArgGPRs > ArgGPRsLeft) { @@ -528,7 +555,7 @@ ABIArgInfo RISCVABIInfo::classifyArgumentType(QualType Ty, bool IsFixed, return ABIArgInfo::getDirect(); } - if (IsSingleCapRecord) + if (ForcePassInCapRegs) return ABIArgInfo::getDirect(); if (const VectorType *VT = Ty->getAs()) @@ -593,21 +620,18 @@ RValue RISCVABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, if (EABI && XLen == 32 && !IsCheriot) TInfo.Align = std::min(TInfo.Align, CharUnits::fromQuantity(4)); - bool IsSingleCapRecord = false; - CharUnits CapabilityWidth = - CharUnits::fromQuantity(getTarget().getCHERICapabilityWidth() / 8); - if (const auto *RT = Ty->getAs()) - IsSingleCapRecord = TInfo.Width == CapabilityWidth && - getContext().containsCapabilities(RT->getDecl()); + uint64_t Size = TInfo.Width.getQuantity() * 8; - bool IsCapability = Ty->isCHERICapabilityType(getContext()) || - IsSingleCapRecord; + auto CapRegsForStruct = getCapRegsForStruct(Size, Ty); + bool IsSingleCapability = + Ty->isCHERICapabilityType(getContext()) || CapRegsForStruct == 1; // Arguments bigger than 2*Xlen bytes are passed indirectly, as are // capabilities for the hybrid ABI. - bool IsIndirect = TInfo.Width > 2 * SlotSize || - (!getContext().getTargetInfo().areAllPointersCapabilities() && - IsCapability); + bool IsIndirect = + TInfo.Width > 2 * SlotSize || + (!getContext().getTargetInfo().areAllPointersCapabilities() && + IsSingleCapability); return emitVoidPtrVAArg(CGF, VAListAddr, Ty, IsIndirect, TInfo, SlotSize, /*AllowHigherAlign=*/true, Slot); diff --git a/clang/test/CodeGen/cheri/cheriot-struct-ret.c b/clang/test/CodeGen/cheri/cheriot-struct-ret.c new file mode 100644 index 0000000000000..9de473733470f --- /dev/null +++ b/clang/test/CodeGen/cheri/cheriot-struct-ret.c @@ -0,0 +1,815 @@ +// RUN: %clang_cc1 %s -o - "-triple" "riscv32cheriot-unknown-cheriotrtos" "-emit-llvm" "-mframe-pointer=none" "-mcmodel=small" "-target-abi" "cheriot" "-O1" "-Werror" "-cheri-compartment=example" -std=c2x | FileCheck %s + +// Test that structs that can fit in two registers are correctly handled, both when used as return values and when passed as an argument. + +#define LENGTH 5 +static unsigned int dummies[] = {1, 2, 3, 4, 5}; +static unsigned int dummy = 0; + +volatile static __attribute__((used)) unsigned int* force_use; + +struct TwoIntegers { + unsigned int one; + unsigned int two; +}; + +__attribute__((cheri_compartment("example"), noinline)) unsigned int GetValue(void) { + return ++dummy; +} + +// CHECK: define dso_local chericcallcce [2 x i32] @_Z8InitIntsv() local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct TwoIntegers InitInts(void) { + // CHECK: entry: + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %call1 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %.fca.0.insert = insertvalue [2 x i32] poison, i32 %call, 0 + // CHECK: %.fca.1.insert = insertvalue [2 x i32] %.fca.0.insert, i32 %call1, 1 + struct TwoIntegers Res = {GetValue(), GetValue()}; + + // CHECK: ret [2 x i32] %.fca.1.insert + return Res; +} + +// CHECK: define dso_local chericcallcce [2 x i32] @_Z7ChgInts11TwoIntegers([2 x i32] %x.coerce) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct TwoIntegers ChgInts(struct TwoIntegers x) { + // CHECK: entry: + // CHECK: %x.coerce.fca.0.extract = extractvalue [2 x i32] %x.coerce, 0 + // CHECK: %x.coerce.fca.1.extract = extractvalue [2 x i32] %x.coerce, 1 + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %sub = sub i32 %x.coerce.fca.0.extract, %call + // CHECK: %call1 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %sub2 = sub i32 %x.coerce.fca.1.extract, %call1 + // CHECK: %.fca.0.insert = insertvalue [2 x i32] poison, i32 %sub, 0 + // CHECK: %.fca.1.insert = insertvalue [2 x i32] %.fca.0.insert, i32 %sub2, 1 + x.one -= GetValue(); + x.two -= GetValue(); + + // CHECK: ret [2 x i32] %.fca.1.insert + return x; +} + + +// Here we want to check that the initial struct received from the callee is handled as a proper struct, i.e. fields are read using `extractvalue` rather than dereferencing pointers. +// CHECK: define dso_local chericcallcce void @_Z9CheckIntsv() local_unnamed_addr addrspace(200) #2 { +__attribute__((cheri_compartment("example"))) void CheckInts() { + + static struct TwoIntegers __attribute__((used)) x = {}; + + // CHECK: entry: + // CHECK: %call = tail call chericcallcce [2 x i32] @_Z8InitIntsv() + // CHECK: %call1 = tail call chericcallcce [2 x i32] @_Z7ChgInts11TwoIntegers([2 x i32] %call) + // CHECK: %call1.fca.0.extract = extractvalue [2 x i32] %call1, 0 + // CHECK: %call1.fca.1.extract = extractvalue [2 x i32] %call1, 1 + // CHECK: store i32 %call1.fca.0.extract, ptr addrspace(200) @_Z9CheckIntsv.x, align 4, !tbaa !7 + // CHECK: store i32 %call1.fca.1.extract, ptr addrspace(200) getelementptr inbounds nuw (i8, ptr addrspace(200) @_Z9CheckIntsv.x, i32 4), align 4, !tbaa !7 + x = ChgInts(InitInts()); + + // CHECK: ret void + return; +} + + +// Do the same but use a struct of two pointers. + +struct TwoPointers { + unsigned int *one; + unsigned int *two; +}; + +// Here we want to check that the result is returned by value. +// CHECK: define dso_local chericcallcce %struct.TwoPointers @_Z8InitPtrsv() local_unnamed_addr addrspace(200) #1 +__attribute__((cheri_compartment("example"), noinline)) struct TwoPointers InitPtrs() { + // CHECK: entry: + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem = urem i32 %call, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + // CHECK: %call1 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem2 = urem i32 %call1, 5 + // CHECK: %add.ptr3 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem2 + struct TwoPointers x = {dummies + (GetValue() % LENGTH), dummies + (GetValue() % LENGTH)}; + + // CHECK: %.fca.0.insert = insertvalue %struct.TwoPointers poison, ptr addrspace(200) %add.ptr, 0 + // CHECK: %.fca.1.insert = insertvalue %struct.TwoPointers %.fca.0.insert, ptr addrspace(200) %add.ptr3, 1 + // CHECK: ret %struct.TwoPointers %.fca.1.insert + return x; +} + +// Here we want to check that the struct received as parameter is laid out as two different arguments and, again, is returned by value. +// CHECK: define dso_local chericcallcce %struct.TwoPointers @_Z7ChgPtrs11TwoPointers(ptr addrspace(200) %x.coerce0, ptr addrspace(200) %x.coerce1) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct TwoPointers ChgPtrs(struct TwoPointers x) { + + // CHECK: entry: + // CHECK: store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.one; + + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem = urem i32 %call, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + x.one = dummies + (GetValue() % LENGTH); + + // CHECK: store ptr addrspace(200) %x.coerce1, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.two; + + // CHECK: %call2 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem3 = urem i32 %call2, 5 + // CHECK: %add.ptr4 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem3 + x.two = dummies + (GetValue() % LENGTH); + + // CHECK: %.fca.0.insert = insertvalue %struct.TwoPointers poison, ptr addrspace(200) %add.ptr, 0 + // CHECK: %.fca.1.insert = insertvalue %struct.TwoPointers %.fca.0.insert, ptr addrspace(200) %add.ptr4, 1 + // CHECK: ret %struct.TwoPointers %.fca.1.insert + return x; +} + +// Here we want to check that the struct received from the callee is handled as a proper struct, i.e. fields are read using `extractvalue` rather than dereferencing pointers. Also, we want to check that arguments in composite calls are passed correctly. +// CHECK: define dso_local chericcallcce void @_Z9CheckPtrsv() local_unnamed_addr addrspace(200) #2 { +__attribute__((cheri_compartment("example"))) void CheckPtrs() { + + static struct TwoPointers __attribute__((used)) x = {}; + + // CHECK: entry: + // CHECK: %call = tail call chericcallcce %struct.TwoPointers @_Z8InitPtrsv() + // CHECK: %0 = extractvalue %struct.TwoPointers %call, 0 + // CHECK: %1 = extractvalue %struct.TwoPointers %call, 1 + // CHECK: %call1 = tail call chericcallcce %struct.TwoPointers @_Z7ChgPtrs11TwoPointers(ptr addrspace(200) %0, ptr addrspace(200) %1) + // CHECK: %2 = extractvalue %struct.TwoPointers %call1, 0 + // CHECK: %3 = extractvalue %struct.TwoPointers %call1, 1 + // CHECK: store ptr addrspace(200) %2, ptr addrspace(200) @_Z9CheckPtrsv.x, align 8, !tbaa !11 + // CHECK: store ptr addrspace(200) %3, ptr addrspace(200) getelementptr inbounds nuw (i8, ptr addrspace(200) @_Z9CheckPtrsv.x, i32 8), align 8, !tbaa !11 + x = ChgPtrs(InitPtrs()); + + // CHECK: ret void + return; +} + + +// Do the same with a pointer and an integer. + +struct PointerAndInt { + unsigned int *one; + unsigned int two; +}; + +// Here we want to check that the result is returned by value. +// CHECK: define dso_local chericcallcce %struct.PointerAndInt @_Z10InitPtrIntv() local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct PointerAndInt InitPtrInt() { + // CHECK: entry: + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem = urem i32 %call, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + // CHECK: %call1 = tail call chericcallcce i32 @_Z8GetValuev() + struct PointerAndInt x = {dummies + (GetValue() % LENGTH), GetValue()}; + + // CHECK: %.fca.0.insert = insertvalue %struct.PointerAndInt poison, ptr addrspace(200) %add.ptr, 0 + // CHECK: %.fca.1.insert = insertvalue %struct.PointerAndInt %.fca.0.insert, i32 %call1, 1 + // CHECK: ret %struct.PointerAndInt %.fca.1.insert + return x; +} + +// Here we want to check that the struct received as parameter is laid out as two different arguments and, again, is returned by value. +// CHECK: define dso_local chericcallcce %struct.PointerAndInt @_Z9ChgPtrInt13PointerAndInt(ptr addrspace(200) %x.coerce0, i32 %x.coerce1) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct PointerAndInt ChgPtrInt(struct PointerAndInt x) { + // CHECK: entry: + // CHECK: store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.one; + + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem = urem i32 %call, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + x.one = dummies + (GetValue() % LENGTH); + + // CHECK: %call2 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %sub = sub i32 %x.coerce1, %call2 + x.two -= GetValue(); + + // CHECK: %.fca.0.insert = insertvalue %struct.PointerAndInt poison, ptr addrspace(200) %add.ptr, 0 + // CHECK: %.fca.1.insert = insertvalue %struct.PointerAndInt %.fca.0.insert, i32 %sub, 1 + // CHECK: ret %struct.PointerAndInt %.fca.1.insert + return x; +} + +// Here we want to check that the struct received from the callee is handled as a proper struct, i.e. fields are read using `extractvalue` rather than dereferencing pointers. Also, we want to check that arguments in composite calls are passed correctly. +// CHECK: define dso_local chericcallcce void @_Z11CheckPtrIntv() local_unnamed_addr addrspace(200) #2 { +__attribute__((cheri_compartment("example"))) void CheckPtrInt() { + + static struct PointerAndInt __attribute__((used)) x = {}; + + // CHECK: entry: + // CHECK: %call = tail call chericcallcce %struct.PointerAndInt @_Z10InitPtrIntv() + // CHECK: %0 = extractvalue %struct.PointerAndInt %call, 0 + // CHECK: %1 = extractvalue %struct.PointerAndInt %call, 1 + // CHECK: %call1 = tail call chericcallcce %struct.PointerAndInt @_Z9ChgPtrInt13PointerAndInt(ptr addrspace(200) %0, i32 %1) + // CHECK: %2 = extractvalue %struct.PointerAndInt %call1, 0 + // CHECK: %3 = extractvalue %struct.PointerAndInt %call1, 1 + // CHECK: store ptr addrspace(200) %2, ptr addrspace(200) @_Z11CheckPtrIntv.x, align 8, !tbaa !11 + // CHECK: store i32 %3, ptr addrspace(200) getelementptr inbounds nuw (i8, ptr addrspace(200) @_Z11CheckPtrIntv.x, i32 8), align 8, !tbaa !7 + x = ChgPtrInt(InitPtrInt()); + + // CHECK: ret void + return; +} + + +// Do the same with an integer and a pointer (order does not matter). + +struct IntAndPointer { + unsigned int one; + unsigned int *two; +}; + +// Here we want to check that the result is returned by value. +// CHECK: define dso_local chericcallcce %struct.IntAndPointer @_Z10InitIntPtrv() local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct IntAndPointer InitIntPtr() { + // CHECK: entry: + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %call1 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem = urem i32 %call1, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + struct IntAndPointer x = {GetValue(), dummies + (GetValue() % LENGTH)}; + + // CHECK: %.fca.0.insert = insertvalue %struct.IntAndPointer poison, i32 %call, 0 + // CHECK: %.fca.1.insert = insertvalue %struct.IntAndPointer %.fca.0.insert, ptr addrspace(200) %add.ptr, 1 + // CHECK: ret %struct.IntAndPointer %.fca.1.insert + return x; +} + +// Here we want to check that the struct received as parameter is laid out as two different arguments and, again, is returned by value. +// CHECK: define dso_local chericcallcce %struct.IntAndPointer @_Z9ChgIntPtr13IntAndPointer(i32 %x.coerce0, ptr addrspace(200) %x.coerce1) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct IntAndPointer ChgIntPtr(struct IntAndPointer x) { + + // CHECK: entry: + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %sub = sub i32 %x.coerce0, %call + x.one -= GetValue(); + + // CHECK: store ptr addrspace(200) %x.coerce1, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.two; + + // CHECK: %call1 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem = urem i32 %call1, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + x.two = dummies + (GetValue() % LENGTH); + + // CHECK: %.fca.0.insert = insertvalue %struct.IntAndPointer poison, i32 %sub, 0 + // CHECK: %.fca.1.insert = insertvalue %struct.IntAndPointer %.fca.0.insert, ptr addrspace(200) %add.ptr, 1 + // CHECK: ret %struct.IntAndPointer %.fca.1.insert + return x; +} + +// Here we want to check that the struct received from the callee is handled as a proper struct, i.e. fields are read using `extractvalue` rather than dereferencing pointers. Also, we want to check that arguments in composite calls are passed correctly. +// CHECK: define dso_local chericcallcce void @_Z11CheckIntPtrv() local_unnamed_addr addrspace(200) #2 { +__attribute__((cheri_compartment("example"))) void CheckIntPtr() { + + static struct IntAndPointer __attribute__((used)) x = {}; + + // CHECK: entry: + // CHECK: %call = tail call chericcallcce %struct.IntAndPointer @_Z10InitIntPtrv() + // CHECK: %0 = extractvalue %struct.IntAndPointer %call, 0 + // CHECK: %1 = extractvalue %struct.IntAndPointer %call, 1 + // CHECK: %call1 = tail call chericcallcce %struct.IntAndPointer @_Z9ChgIntPtr13IntAndPointer(i32 %0, ptr addrspace(200) %1) + // CHECK: %2 = extractvalue %struct.IntAndPointer %call1, 0 + // CHECK: %3 = extractvalue %struct.IntAndPointer %call1, 1 + // CHECK: store i32 %2, ptr addrspace(200) @_Z11CheckIntPtrv.x, align 8, !tbaa !7 + // CHECK: store ptr addrspace(200) %3, ptr addrspace(200) getelementptr inbounds nuw (i8, ptr addrspace(200) @_Z11CheckIntPtrv.x, i32 8), align 8, !tbaa !11 + x = ChgIntPtr(InitIntPtr()); + + // CHECK: ret void + return; +} + + +// What happens in this case? + +struct Inner { + unsigned int z; +}; + +struct Parent { + unsigned int x; + struct Inner y; +}; + + +// Here we want to check that the result is returned by value. +// CHECK: define dso_local chericcallcce [2 x i32] @_Z10InitParentv() local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct Parent InitParent() { + // CHECK: entry: + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %call1 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %.fca.0.insert = insertvalue [2 x i32] poison, i32 %call, 0 + // CHECK: %.fca.1.insert = insertvalue [2 x i32] %.fca.0.insert, i32 %call1, 1 + struct Parent x = {GetValue(), {GetValue()}}; + + // CHECK: ret [2 x i32] %.fca.1.insert + return x; +} + +// Here we want to check that the struct received as parameter is laid out as two different arguments and, again, is returned by value. +// CHECK: define dso_local chericcallcce [2 x i32] @_Z9ChgParent6Parent([2 x i32] %x.coerce) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct Parent ChgParent(struct Parent x) { + + + + + // CHECK: entry: + // CHECK: %x.coerce.fca.0.extract = extractvalue [2 x i32] %x.coerce, 0 + // CHECK: %x.coerce.fca.1.extract = extractvalue [2 x i32] %x.coerce, 1 + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %sub = sub i32 %x.coerce.fca.0.extract, %call + x.x -= GetValue(); + + // CHECK: %call2 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %sub3 = sub i32 %x.coerce.fca.1.extract, %call2 + x.y.z -= GetValue(); + + // CHECK: %.fca.0.insert = insertvalue [2 x i32] poison, i32 %sub, 0 + // CHECK: %.fca.1.insert = insertvalue [2 x i32] %.fca.0.insert, i32 %sub3, 1 + // CHECK: ret [2 x i32] %.fca.1.insert + return x; +} + +// Here we want to check that the struct received from the callee is handled as a proper struct, i.e. fields are read using `extractvalue` rather than dereferencing pointers. Also, we want to check that arguments in composite calls are passed correctly. +// CHECK: define dso_local chericcallcce void @_Z11CheckParentv() local_unnamed_addr addrspace(200) #2 { +__attribute__((cheri_compartment("example"))) void CheckParent() { + + static struct Parent __attribute__((used)) x = {}; + + // CHECK: entry: + // CHECK: %call = tail call chericcallcce [2 x i32] @_Z10InitParentv() + // CHECK: %call1 = tail call chericcallcce [2 x i32] @_Z9ChgParent6Parent([2 x i32] %call) + // CHECK: %call1.fca.0.extract = extractvalue [2 x i32] %call1, 0 + // CHECK: %call1.fca.1.extract = extractvalue [2 x i32] %call1, 1 + // CHECK: store i32 %call1.fca.0.extract, ptr addrspace(200) @_Z11CheckParentv.x, align 4, !tbaa !7 + // CHECK: store i32 %call1.fca.1.extract, ptr addrspace(200) getelementptr inbounds nuw (i8, ptr addrspace(200) @_Z11CheckParentv.x, i32 4), align 4, !tbaa !7 + x = ChgParent(InitParent()); + + // CHECK: ret void + return; +} + +struct InnerPtr { + unsigned int* z; +}; + +struct ParentPtr { + unsigned int* x; + struct InnerPtr y; +}; + + +// Here we want to check that the result is returned by value. +// CHECK: define dso_local chericcallcce %struct.ParentPtr @_Z13InitParentPtrv() local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct ParentPtr InitParentPtr() { + // CHECK: entry: + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem = urem i32 %call, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + // CHECK: %call1 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem2 = urem i32 %call1, 5 + // CHECK: %add.ptr3 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem2 + // CHECK: %.fca.0.insert = insertvalue %struct.ParentPtr poison, ptr addrspace(200) %add.ptr, 0 + // CHECK: %.fca.1.0.insert = insertvalue %struct.ParentPtr %.fca.0.insert, ptr addrspace(200) %add.ptr3, 1, 0 + struct ParentPtr x = {dummies + (GetValue() % LENGTH), {dummies + (GetValue() % LENGTH)}}; + + // CHECK: ret %struct.ParentPtr %.fca.1.0.insert + return x; +} + +// Here we want to check that the struct received as parameter is laid out as two different arguments and, again, is returned by value. +// CHECK: define dso_local chericcallcce %struct.ParentPtr @_Z12ChgParentPtr9ParentPtr(ptr addrspace(200) %x.coerce0, %struct.InnerPtr %x.coerce1) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct ParentPtr ChgParentPtr(struct ParentPtr x) { + // CHECK: entry: + // CHECK: %x.coerce1.fca.0.extract = extractvalue %struct.InnerPtr %x.coerce1, 0 + + // CHECK: store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.x; + + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem = urem i32 %call, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + x.x = dummies + (GetValue() % LENGTH); + + // CHECK: store ptr addrspace(200) %x.coerce1.fca.0.extract, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.y.z; + + // CHECK: %call3 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem4 = urem i32 %call3, 5 + // CHECK: %add.ptr5 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem4 + x.y.z = dummies + (GetValue() % LENGTH); + + // CHECK: %.fca.0.insert = insertvalue %struct.ParentPtr poison, ptr addrspace(200) %add.ptr, 0 + // CHECK: %.fca.1.0.insert = insertvalue %struct.ParentPtr %.fca.0.insert, ptr addrspace(200) %add.ptr5, 1, 0 + // CHECK: ret %struct.ParentPtr %.fca.1.0.insert + return x; +} + +// Here we want to check that the struct received from the callee is handled as a proper struct, i.e. fields are read using `extractvalue` rather than dereferencing pointers. Also, we want to check that arguments in composite calls are passed correctly. +// CHECK: define dso_local chericcallcce void @_Z14CheckParentPtrv() local_unnamed_addr addrspace(200) #2 { +__attribute__((cheri_compartment("example"))) void CheckParentPtr() { + + static struct ParentPtr __attribute__((used)) x = {}; + + // CHECK: entry: + // CHECK: %call = tail call chericcallcce %struct.ParentPtr @_Z13InitParentPtrv() + // CHECK: %0 = extractvalue %struct.ParentPtr %call, 0 + // CHECK: %1 = extractvalue %struct.ParentPtr %call, 1 + // CHECK: %call1 = tail call chericcallcce %struct.ParentPtr @_Z12ChgParentPtr9ParentPtr(ptr addrspace(200) %0, %struct.InnerPtr %1) + // CHECK: %2 = extractvalue %struct.ParentPtr %call1, 0 + // CHECK: %3 = extractvalue %struct.ParentPtr %call1, 1 + // CHECK: %.fca.0.extract3 = extractvalue %struct.InnerPtr %3, 0 + // CHECK: store ptr addrspace(200) %2, ptr addrspace(200) @_Z14CheckParentPtrv.x, align 8, !tbaa !11 + // CHECK: store ptr addrspace(200) %.fca.0.extract3, ptr addrspace(200) getelementptr inbounds nuw (i8, ptr addrspace(200) @_Z14CheckParentPtrv.x, i32 8), align 8, !tbaa !11 + x = ChgParentPtr(InitParentPtr()); + + // CHECK: ret void + return; +} + +// For arguments, does it work when the struct is placed in an odd-numbered position? + +// CHECK: define dso_local chericcallcce [2 x i32] @_Z8ChgInts2i11TwoIntegers(i32 noundef %new_int, [2 x i32] %x.coerce) local_unnamed_addr addrspace(200) #4 { +__attribute__((cheri_compartment("example"), noinline)) struct TwoIntegers ChgInts2(int new_int, struct TwoIntegers x) { + // CHECK: %x.coerce.fca.0.extract = extractvalue [2 x i32] %x.coerce, 0 + // CHECK: %x.coerce.fca.1.extract = extractvalue [2 x i32] %x.coerce, 1 + // CHECK: %add = add i32 %x.coerce.fca.0.extract, %new_int + x.one += new_int; + + // CHECK: %add1 = add i32 %x.coerce.fca.1.extract, %new_int + x.two += new_int; + + // CHECK: %.fca.0.insert = insertvalue [2 x i32] poison, i32 %add, 0 + // CHECK: %.fca.1.insert = insertvalue [2 x i32] %.fca.0.insert, i32 %add1, 1 + // CHECK: ret [2 x i32] %.fca.1.insert + return x; +} + +// CHECK: define dso_local chericcallcce %struct.TwoPointers @_Z8ChgPtrs2i11TwoPointers(i32 noundef %new_int, ptr addrspace(200) %x.coerce0, ptr addrspace(200) %x.coerce1) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct TwoPointers ChgPtrs2(int new_int, struct TwoPointers x) { + // CHECK: entry: + // CHECK: store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.one; + + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %add = add i32 %call, %new_int + // CHECK: %rem = urem i32 %add, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + x.one = dummies + ((GetValue() + new_int) % LENGTH); + + // CHECK: store ptr addrspace(200) %x.coerce1, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.two; + + // CHECK: %call2 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %add3 = add i32 %call2, %new_int + // CHECK: %rem4 = urem i32 %add3, 5 + // CHECK: %add.ptr5 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem4 + x.two = dummies + ((GetValue() + new_int) % LENGTH); + + // CHECK: %.fca.0.insert = insertvalue %struct.TwoPointers poison, ptr addrspace(200) %add.ptr, 0 + // CHECK: %.fca.1.insert = insertvalue %struct.TwoPointers %.fca.0.insert, ptr addrspace(200) %add.ptr5, 1 + // CHECK: ret %struct.TwoPointers %.fca.1.insert + return x; +} + +// CHECK: define dso_local chericcallcce %struct.ParentPtr @_Z13ChgParentPtr2i9ParentPtr(i32 noundef %new_int, ptr addrspace(200) %x.coerce0, %struct.InnerPtr %x.coerce1) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct ParentPtr ChgParentPtr2(int new_int, struct ParentPtr x) { + + // CHECK: entry: + // CHECK: %x.coerce1.fca.0.extract = extractvalue %struct.InnerPtr %x.coerce1, 0 + + // CHECK: store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.x; + + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %add = add i32 %call, %new_int + // CHECK: %rem = urem i32 %add, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + x.x = dummies + ((GetValue() + new_int) % LENGTH); + + // CHECK: store ptr addrspace(200) %x.coerce1.fca.0.extract, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.y.z; + + // CHECK: %call3 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %add4 = add i32 %call3, %new_int + // CHECK: %rem5 = urem i32 %add4, 5 + // CHECK: %add.ptr6 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem5 + x.y.z = dummies + ((GetValue() + new_int) % LENGTH); + + // CHECK: %.fca.0.insert = insertvalue %struct.ParentPtr poison, ptr addrspace(200) %add.ptr, 0 + // CHECK: %.fca.1.0.insert = insertvalue %struct.ParentPtr %.fca.0.insert, ptr addrspace(200) %add.ptr6, 1, 0 + // CHECK: ret %struct.ParentPtr %.fca.1.0.insert + return x; +} + + +// For arguments, does it work when the function is variadic? +// CHECK: define dso_local chericcallcce [2 x i32] @_Z8ChgInts3i11TwoIntegersz(i32 noundef %n, [2 x i32] %x.coerce, ...) local_unnamed_addr addrspace(200) #5 { +__attribute__((cheri_compartment("example"), noinline)) struct TwoIntegers ChgInts3(int n, struct TwoIntegers x, ...) { + __builtin_va_list args; + + // CHECK: call void @llvm.va_start.p200(ptr addrspace(200) nonnull %args) + __builtin_va_start(args, x); + + for (int i = 0; i < n; i++) { + + int v = __builtin_va_arg(args, int); + + // CHECK: for.cond.cleanup: ; preds = %for.cond.cleanup.loopexit, %entry + // CHECK: %.fca.1.insert.merged = phi [2 x i32] [ %x.coerce, %entry ], [ %1, %for.cond.cleanup.loopexit ] + // CHECK: call void @llvm.va_end.p200(ptr addrspace(200) %args) + // CHECK: call void @llvm.lifetime.end.p200(i64 8, ptr addrspace(200) nonnull %args) #8 + // CHECK: ret [2 x i32] %.fca.1.insert.merged + + x.one += v; + x.two += v; + } + + __builtin_va_end(args); + + return x; +} + +// CHECK: define dso_local chericcallcce %struct.TwoPointers @_Z8ChgPtrs3i11TwoPointersz(i32 noundef %n, ptr addrspace(200) %x.coerce0, ptr addrspace(200) %x.coerce1, ...) local_unnamed_addr addrspace(200) #7 { +__attribute__((cheri_compartment("example"), noinline)) struct TwoPointers ChgPtrs3(int n, struct TwoPointers x, ...) { + + __builtin_va_list args; + + __builtin_va_start(args, x); + + // CHECK: store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.one; + + // -- just to force the line above -- + volatile int _ = GetValue(); + + // CHECK: store ptr addrspace(200) %x.coerce1, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.two; + + for (int i = 0; i < n; i++) { + + int v = __builtin_va_arg(args, int); + + // CHECK: for.cond.cleanup: ; preds = %for.cond.for.cond.cleanup_crit_edge, %entry + // CHECK: %x.sroa.0.0.lcssa = phi ptr addrspace(200) [ %add.ptr.le, %for.cond.for.cond.cleanup_crit_edge ], [ %x.coerce0, %entry ] + // CHECK: %x.sroa.4.0.lcssa = phi ptr addrspace(200) [ %add.ptr6.le, %for.cond.for.cond.cleanup_crit_edge ], [ %x.coerce1, %entry ] + // CHECK: call void @llvm.va_end.p200(ptr addrspace(200) %args) + // CHECK: call void @llvm.lifetime.end.p200(i64 4, ptr addrspace(200) nonnull %_) + // CHECK: call void @llvm.lifetime.end.p200(i64 8, ptr addrspace(200) nonnull %args) #8 + // CHECK: %.fca.0.insert = insertvalue %struct.TwoPointers poison, ptr addrspace(200) %x.sroa.0.0.lcssa, 0 + // CHECK: %.fca.1.insert = insertvalue %struct.TwoPointers %.fca.0.insert, ptr addrspace(200) %x.sroa.4.0.lcssa, 1 + // CHECK: ret %struct.TwoPointers %.fca.1.insert + + + x.one = dummies + ((GetValue() + v) % LENGTH); + x.two = dummies + ((GetValue() + v) % LENGTH); + } + + __builtin_va_end(args); + + return x; +} + +// CHECK: define dso_local chericcallcce %struct.ParentPtr @_Z13ChgParentPtr3i9ParentPtrz(i32 noundef %n, ptr addrspace(200) %x.coerce0, %struct.InnerPtr %x.coerce1, ...) local_unnamed_addr addrspace(200) #7 { +__attribute__((cheri_compartment("example"), noinline)) struct ParentPtr ChgParentPtr3(int n, struct ParentPtr x, ...) { + + __builtin_va_list args; + + __builtin_va_start(args, x); + + // CHECK: store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.x; + + // -- just to force the line above -- + volatile int _ = GetValue(); + + // CHECK: store ptr addrspace(200) %x.coerce1.fca.0.extract, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.y.z; + + for (int i = 0; i < n; i++) { + + int v = __builtin_va_arg(args, int); + + // CHECK: for.cond.cleanup: ; preds = %for.cond.for.cond.cleanup_crit_edge, %entry + // CHECK: %x.sroa.0.0.lcssa = phi ptr addrspace(200) [ %add.ptr.le, %for.cond.for.cond.cleanup_crit_edge ], [ %x.coerce0, %entry ] + // CHECK: %x.sroa.4.0.lcssa = phi ptr addrspace(200) [ %add.ptr7.le, %for.cond.for.cond.cleanup_crit_edge ], [ %x.coerce1.fca.0.extract, %entry ] + // CHECK: call void @llvm.va_end.p200(ptr addrspace(200) %args) + // CHECK: call void @llvm.lifetime.end.p200(i64 4, ptr addrspace(200) nonnull %_) + // CHECK: call void @llvm.lifetime.end.p200(i64 8, ptr addrspace(200) nonnull %args) #8 + // CHECK: %.fca.0.insert = insertvalue %struct.ParentPtr poison, ptr addrspace(200) %x.sroa.0.0.lcssa, 0 + // CHECK: %.fca.1.0.insert = insertvalue %struct.ParentPtr %.fca.0.insert, ptr addrspace(200) %x.sroa.4.0.lcssa, 1, 0 + // CHECK: ret %struct.ParentPtr %.fca.1.0.insert + + x.x = dummies + ((GetValue() + v) % LENGTH); + x.y.z = dummies + ((GetValue() + v) % LENGTH); + } + + __builtin_va_end(args); + + return x; +} + + +// For arguments, does it work correctly when the "optimizable" argument sits across the "put in registers" and "spill to stack" boundary? + +// CHECK: define dso_local chericcallcce [2 x i32] @_Z8ChgInts4iiiii11TwoIntegers(i32 noundef %n0, i32 noundef %n1, i32 noundef %n2, i32 noundef %n3, i32 noundef %n4, [2 x i32] %x.coerce) local_unnamed_addr addrspace(200) #4 { +__attribute__((cheri_compartment("example"), noinline)) struct TwoIntegers ChgInts4(int n0, int n1, int n2, int n3, int n4, struct TwoIntegers x) { + + // CHECK: entry: + // CHECK: %x.coerce.fca.0.extract = extractvalue [2 x i32] %x.coerce, 0 + // CHECK: %x.coerce.fca.1.extract = extractvalue [2 x i32] %x.coerce, 1 + + // CHECK: %add = add nsw i32 %n1, %n0 + // CHECK: %add1 = add nsw i32 %add, %n2 + // CHECK: %add2 = add nsw i32 %add1, %n3 + // CHECK: %add3 = add nsw i32 %add2, %n4 + // CHECK: %add4 = add i32 %x.coerce.fca.0.extract, %add3 + x.one += n0 + n1 + n2 + n3 + n4; + + // CHECK: %add9 = add i32 %x.coerce.fca.1.extract, %add3 + x.two += n0 + n1 + n2 + n3 + n4; + + // CHECK: %.fca.0.insert = insertvalue [2 x i32] poison, i32 %add4, 0 + // CHECK: %.fca.1.insert = insertvalue [2 x i32] %.fca.0.insert, i32 %add9, 1 + // CHECK: ret [2 x i32] %.fca.1.insert + return x; +} + +// CHECK: define dso_local chericcallcce %struct.TwoPointers @_Z8ChgPtrs4iiiii11TwoPointers(i32 noundef %n0, i32 noundef %n1, i32 noundef %n2, i32 noundef %n3, i32 noundef %n4, ptr addrspace(200) %x.coerce0, ptr addrspace(200) %x.coerce1) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct TwoPointers ChgPtrs4(int n0, int n1, int n2, int n3, int n4, struct TwoPointers x) { + + // CHECK: entry: + // CHECK: store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.one; + + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %add = add i32 %n1, %n0 + // CHECK: %add1 = add i32 %add, %n2 + // CHECK: %add2 = add i32 %add1, %n3 + // CHECK: %add3 = add i32 %add2, %n4 + // CHECK: %add4 = add i32 %add3, %call + // CHECK: %rem = urem i32 %add4, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + x.one = dummies + ((GetValue() + n0 + n1 + n2 + n3 + n4) % LENGTH); + + // CHECK: store ptr addrspace(200) %x.coerce1, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.two; + + // CHECK: %call6 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %add7 = add i32 %n1, %n0 + // CHECK: %add8 = add i32 %add7, %n2 + // CHECK: %add9 = add i32 %add8, %n3 + // CHECK: %add10 = add i32 %add9, %n4 + // CHECK: %add11 = add i32 %add10, %call6 + // CHECK: %rem12 = urem i32 %add11, 5 + // CHECK: %add.ptr13 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem12 + x.two = dummies + ((GetValue() + n0 + n1 + n2 + n3 + n4) % LENGTH); + + // CHECK: %.fca.0.insert = insertvalue %struct.TwoPointers poison, ptr addrspace(200) %add.ptr, 0 + // CHECK: %.fca.1.insert = insertvalue %struct.TwoPointers %.fca.0.insert, ptr addrspace(200) %add.ptr13, 1 + // CHECK: ret %struct.TwoPointers %.fca.1.insert + return x; +} + +// CHECK: define dso_local chericcallcce %struct.ParentPtr @_Z13ChgParentPtr4iiiii9ParentPtr(i32 noundef %n0, i32 noundef %n1, i32 noundef %n2, i32 noundef %n3, i32 noundef %n4, ptr addrspace(200) %x.coerce0, %struct.InnerPtr %x.coerce1) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct ParentPtr ChgParentPtr4(int n0, int n1, int n2, int n3, int n4, struct ParentPtr x) { + + // CHECK: entry: + // CHECK: %x.coerce1.fca.0.extract = extractvalue %struct.InnerPtr %x.coerce1, 0 + + + // CHECK: store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.x; + + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %add = add i32 %n1, %n0 + // CHECK: %add2 = add i32 %add, %n2 + // CHECK: %add3 = add i32 %add2, %n3 + // CHECK: %add4 = add i32 %add3, %n4 + // CHECK: %add5 = add i32 %add4, %call + // CHECK: %rem = urem i32 %add5, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + + + x.x= dummies + ((GetValue() + n0 + n1 + n2 + n3 + n4) % LENGTH); + + // CHECK: store ptr addrspace(200) %x.coerce1.fca.0.extract, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.y.z; + + // CHECK: %call7 = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %add8 = add i32 %n1, %n0 + // CHECK: %add9 = add i32 %add8, %n2 + // CHECK: %add10 = add i32 %add9, %n3 + // CHECK: %add11 = add i32 %add10, %n4 + // CHECK: %add12 = add i32 %add11, %call7 + // CHECK: %rem13 = urem i32 %add12, 5 + // CHECK: %add.ptr14 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem13 + x.y.z = dummies + ((GetValue() + n0 + n1 + n2 + n3 + n4) % LENGTH); + + // CHECK: %.fca.0.insert = insertvalue %struct.ParentPtr poison, ptr addrspace(200) %add.ptr, 0 + // CHECK: %.fca.1.0.insert = insertvalue %struct.ParentPtr %.fca.0.insert, ptr addrspace(200) %add.ptr14, 1, 0 + + + // CHECK: ret %struct.ParentPtr %.fca.1.0.insert + return x; +} + +// One-sized checks. + +struct OneInt { + unsigned int x; +}; + +// CHECK: } + +// CHECK: define dso_local chericcallcce i32 @_Z10InitOneIntv() local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct OneInt InitOneInt(void) { + + // CHECK: entry: + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + struct OneInt Res = {GetValue()}; + + // CHECK: ret i32 %call + return Res; +} + +// CHECK: define dso_local chericcallcce i32 @_Z9ChgOneInt6OneInt(i32 %x.coerce) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct OneInt ChgOneInt(struct OneInt x) { + // CHECK: entry: + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %sub = sub i32 %x.coerce, %call + x.x -= GetValue(); + + // CHECK: ret i32 %sub + return x; +} + + +// Here we want to check that the initial struct received from the callee is handled as a proper struct, i.e. fields are read using `extractvalue` rather than dereferencing pointers. +// CHECK: define dso_local chericcallcce void @_Z11CheckOneIntv() local_unnamed_addr addrspace(200) #2 { +__attribute__((cheri_compartment("example"))) void CheckOneInt() { + + static struct OneInt __attribute__((used)) x = {}; + + // CHECK: entry: + // CHECK: %call = tail call chericcallcce i32 @_Z10InitOneIntv() + // CHECK: %call2 = tail call chericcallcce i32 @_Z9ChgOneInt6OneInt(i32 %call) + // CHECK: store i32 %call2, ptr addrspace(200) @_Z11CheckOneIntv.x, align 4, !tbaa !7 + x = ChgOneInt(InitOneInt()); + + // CHECK: ret void + return; +} + +struct OnePtr { + unsigned int* x; +}; + +// CHECK: define dso_local chericcallcce %struct.OnePtr @_Z10InitOnePtrv() local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct OnePtr InitOnePtr(void) { + // CHECK: entry: + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem = urem i32 %call, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + // CHECK: %.fca.0.insert = insertvalue %struct.OnePtr poison, ptr addrspace(200) %add.ptr, 0 + struct OnePtr Res = {dummies + (GetValue() % LENGTH)}; + + // CHECK: ret %struct.OnePtr %.fca.0.insert + return Res; +} + +// CHECK: define dso_local chericcallcce %struct.OnePtr @_Z9ChgOnePtr6OnePtr(ptr addrspace(200) %x.coerce) local_unnamed_addr addrspace(200) #1 { +__attribute__((cheri_compartment("example"), noinline)) struct OnePtr ChgOnePtr(struct OnePtr x) { + // CHECK: entry: + // CHECK: store ptr addrspace(200) %x.coerce, ptr addrspace(200) @force_use, align 8, !tbaa !11 + force_use = x.x; + + // CHECK: %call = tail call chericcallcce i32 @_Z8GetValuev() + // CHECK: %rem = urem i32 %call, 5 + // CHECK: %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + // CHECK: %.fca.0.insert = insertvalue %struct.OnePtr poison, ptr addrspace(200) %add.ptr, 0 + x.x = dummies + (GetValue() % LENGTH); + + // CHECK: ret %struct.OnePtr %.fca.0.insert + return x; +} + +// CHECK: define dso_local chericcallcce void @_Z11CheckOnePtrv() local_unnamed_addr addrspace(200) #2 { +__attribute__((cheri_compartment("example"))) void CheckOnePtr () { + + static struct OnePtr __attribute__((used)) x = {}; + + // CHECK: entry: + // CHECK: %call = tail call chericcallcce %struct.OnePtr @_Z10InitOnePtrv() + // CHECK: %0 = extractvalue %struct.OnePtr %call, 0 + // CHECK: %call1 = tail call chericcallcce %struct.OnePtr @_Z9ChgOnePtr6OnePtr(ptr addrspace(200) %0) + // CHECK: %1 = extractvalue %struct.OnePtr %call1, 0 + // CHECK: store ptr addrspace(200) %1, ptr addrspace(200) @_Z11CheckOnePtrv.x, align 8, !tbaa !11 + x = ChgOnePtr(InitOnePtr()); + + // CHECK: ret void + return; +} + + +// CHECK: attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, argmem: none, inaccessiblemem: none) "cheri-compartment"="example" "interrupt-state"="enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+32bit,+c,+cap-mode,+e,+m,+xcheri" } +// CHECK: attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) "cheri-compartment"="example" "interrupt-state"="enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+32bit,+c,+cap-mode,+e,+m,+xcheri" } +// CHECK: attributes #2 = { mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) "cheri-compartment"="example" "interrupt-state"="enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+32bit,+c,+cap-mode,+e,+m,+xcheri" } diff --git a/llvm/test/CodeGen/RISCV/cheri/cheriot-struct-ret.ll b/llvm/test/CodeGen/RISCV/cheri/cheriot-struct-ret.ll new file mode 100644 index 0000000000000..cd18b779ba468 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/cheri/cheriot-struct-ret.ll @@ -0,0 +1,770 @@ +; RUN: llc --filetype=asm --mcpu=cheriot --mtriple=riscv32cheriot-unknown-cheriotrtos -target-abi cheriot %s -mattr=+xcheri,+cap-mode,+xcheriot -o - | 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.TwoIntegers = type { i32, i32 } +%struct.TwoPointers = type { ptr addrspace(200), ptr addrspace(200) } +%struct.InnerPtr = type { ptr addrspace(200) } +%struct.ParentPtr = type { ptr addrspace(200), %struct.InnerPtr } +%struct.PointerAndInt = type { ptr addrspace(200), i32 } + +@dummy = internal unnamed_addr addrspace(200) global i32 0, align 4 +@_Z9CheckIntsv.x = internal addrspace(200) global %struct.TwoIntegers zeroinitializer, align 4 +@dummies = internal addrspace(200) global [5 x i32] [i32 1, i32 2, i32 3, i32 4, i32 5], align 4 +@force_use = internal addrspace(200) global ptr addrspace(200) null, align 8 +@_Z9CheckPtrsv.x = internal addrspace(200) global %struct.TwoPointers zeroinitializer, align 8 +@_Z11CheckPtrIntv.x = internal addrspace(200) global { ptr addrspace(200), i32, [4 x i8] } zeroinitializer, align 8 +@_Z14CheckParentPtrv.x = internal addrspace(200) global %struct.ParentPtr zeroinitializer, align 8 +@llvm.compiler.used = appending addrspace(200) global [5 x ptr addrspace(200)] [ptr addrspace(200) @_Z11CheckPtrIntv.x, ptr addrspace(200) @_Z14CheckParentPtrv.x, ptr addrspace(200) @_Z9CheckIntsv.x, ptr addrspace(200) @_Z9CheckPtrsv.x, ptr addrspace(200) @force_use], section "llvm.metadata" + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, argmem: none, inaccessiblemem: none) +define dso_local chericcallcce i32 @_Z8GetValuev() local_unnamed_addr addrspace(200) #0 { +entry: + %0 = load i32, ptr addrspace(200) @dummy, align 4, !tbaa !7 + %inc = add i32 %0, 1 + store i32 %inc, ptr addrspace(200) @dummy, align 4, !tbaa !7 + ret i32 %inc +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce [2 x i32] @_Z8InitIntsv() local_unnamed_addr addrspace(200) #1 { + +;; CHECK: _Z8InitIntsv: # @_Z8InitIntsv +entry: + + ;; CHECK: ct.ccall _Z8GetValuev + ;; CHECK: mv s0, a0 + %call = tail call chericcallcce i32 @_Z8GetValuev() + + ;; CHECK: ct.ccall _Z8GetValuev + ;; CHECK: mv a1, a0 + ;; CHECK: mv a0, s0 + %call1 = tail call chericcallcce i32 @_Z8GetValuev() + %.fca.0.insert = insertvalue [2 x i32] poison, i32 %call, 0 + %.fca.1.insert = insertvalue [2 x i32] %.fca.0.insert, i32 %call1, 1 + + ;; Usual callee epilogue. + ;; CHECK-NEXT: ct.clc cra, 8(csp) # 8-byte Folded Reload + ;; CHECK-NEXT: ct.clc cs0, 0(csp) # 8-byte Folded Reload + ;; CHECK-NEXT: ct.cincoffset csp, csp, 16 + ;; CHECK-NEXT: ct.cret + ret [2 x i32] %.fca.1.insert +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce [2 x i32] @_Z7ChgInts11TwoIntegers([2 x i32] %x.coerce) local_unnamed_addr addrspace(200) #1 { + +;; CHECK: _Z7ChgInts11TwoIntegers: # @_Z7ChgInts11TwoIntegers +entry: + ;; CHECK: mv s0, a1 + ;; CHECK: mv s1, a0 + %x.coerce.fca.0.extract = extractvalue [2 x i32] %x.coerce, 0 + %x.coerce.fca.1.extract = extractvalue [2 x i32] %x.coerce, 1 + + ;; CHECK: ct.ccall _Z8GetValuev + %call = tail call chericcallcce i32 @_Z8GetValuev() + + ;; CHECK: sub s1, s1, a0 + %sub = sub i32 %x.coerce.fca.0.extract, %call + + ;; CHECK: ct.ccall _Z8GetValuev + %call1 = tail call chericcallcce i32 @_Z8GetValuev() + + ;; CHECK: sub a1, s0, a0 + %sub2 = sub i32 %x.coerce.fca.1.extract, %call1 + + %.fca.0.insert = insertvalue [2 x i32] poison, i32 %sub, 0 + %.fca.1.insert = insertvalue [2 x i32] %.fca.0.insert, i32 %sub2, 1 + + ;; CHECK: mv a0, s1 + ;; CHECK: ct.clc cra, 24(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc cs0, 16(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc cs1, 8(csp) # 8-byte Folded Reload + ;; CHECK: ct.cincoffset csp, csp, 32 + ;; CHECK: ct.cret + ret [2 x i32] %.fca.1.insert +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce void @_Z9CheckIntsv() local_unnamed_addr addrspace(200) #2 { +entry: + %call = tail call chericcallcce [2 x i32] @_Z8InitIntsv() + %call1 = tail call chericcallcce [2 x i32] @_Z7ChgInts11TwoIntegers([2 x i32] %call) + + ;; CHECK: _Z9CheckIntsv: # @_Z9CheckIntsv + ;; CHECK: ct.ccall _Z8InitIntsv + ;; CHECK-NEXT: ct.ccall _Z7ChgInts11TwoIntegers + + %call1.fca.0.extract = extractvalue [2 x i32] %call1, 0 + %call1.fca.1.extract = extractvalue [2 x i32] %call1, 1 + store i32 %call1.fca.0.extract, ptr addrspace(200) @_Z9CheckIntsv.x, align 4, !tbaa !7 + store i32 %call1.fca.1.extract, ptr addrspace(200) getelementptr inbounds nuw (i8, ptr addrspace(200) @_Z9CheckIntsv.x, i32 4), align 4, !tbaa !7 + ret void +} + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.start.p200(i64 immarg, ptr addrspace(200) nocapture) addrspace(200) #3 + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.end.p200(i64 immarg, ptr addrspace(200) nocapture) addrspace(200) #3 + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce %struct.TwoPointers @_Z8InitPtrsv() local_unnamed_addr addrspace(200) #1 { +;; CHECK: _Z8InitPtrsv: # @_Z8InitPtrsv +entry: + + ;; CHECK: ct.ccall _Z8GetValuev + %call = tail call chericcallcce i32 @_Z8GetValuev() + + ;; CHECK: auicgp cs0, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset cs0, cs0, %cheriot_compartment_lo_i(.LBB4_1) + ;; CHECK: ct.csetbounds cs0, cs0, %cheriot_compartment_size(dummies) + ;; CHECK: ct.cincoffset ca0, cs0, a0 + %rem = urem i32 %call, 5 + %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + + ;; CHECK: ct.ccall _Z8GetValuev + ;; CHECK: ct.cincoffset ca1, cs0, a0 + %call1 = tail call chericcallcce i32 @_Z8GetValuev() + %rem2 = urem i32 %call1, 5 + %add.ptr3 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem2 + %.fca.0.insert = insertvalue %struct.TwoPointers poison, ptr addrspace(200) %add.ptr, 0 + %.fca.1.insert = insertvalue %struct.TwoPointers %.fca.0.insert, ptr addrspace(200) %add.ptr3, 1 + + ;; CHECK: ct.clc ca0, 0(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc cra, 24(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc cs0, 16(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc cs1, 8(csp) # 8-byte Folded Reload + ;; CHECK: ct.cincoffset csp, csp, 32 + ;; CHECK: ct.cret + ret %struct.TwoPointers %.fca.1.insert +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce %struct.TwoPointers @_Z7ChgPtrs11TwoPointers(ptr addrspace(200) %x.coerce0, ptr addrspace(200) %x.coerce1) local_unnamed_addr addrspace(200) #1 { +;; CHECK: _Z7ChgPtrs11TwoPointers: # @_Z7ChgPtrs11TwoPointers +;; CHECK: ct.csc ca1, 16(csp) # 8-byte Folded Spill +entry: + + ;; CHECK: auicgp ca1, %cheriot_compartment_hi(force_use) + ;; CHECK: cincoffset ca1, ca1, %cheriot_compartment_lo_i(.LBB5_1) + ;; CHECK: ct.csc ca1, 8(csp) # 8-byte Folded Spill + ;; CHECK: ct.csc ca0, 0(ca1) + store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + %call = tail call chericcallcce i32 @_Z8GetValuev() + %rem = urem i32 %call, 5 + + ;; %call above was saved/reloaded in a0 + ;; CHECK: auicgp cs0, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset cs0, cs0, %cheriot_compartment_lo_i(.LBB5_2) + ;; CHECK: ct.csetbounds cs0, cs0, %cheriot_compartment_size(dummies) + ;; CHECK: sub a0, a0, a1 + ;; CHECK: slli a0, a0, 2 + ;; CHECK: ct.cincoffset ca0, cs0, a0 + ;; CHECK: ct.csc ca0, 0(csp) # 8-byte Folded Spill + %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + + ;; CHECK: ct.clc ca0, 16(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc ca1, 8(csp) # 8-byte Folded Reload + ;; CHECK: ct.csc ca0, 0(ca1) + store ptr addrspace(200) %x.coerce1, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + %call2 = tail call chericcallcce i32 @_Z8GetValuev() + %rem3 = urem i32 %call2, 5 + + ;; CHECK: ct.cincoffset ca1, cs0, a0 + %add.ptr4 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem3 + + ;; ca1 already contains the final value; reload ca0 + ;; CHECK: ct.clc ca0, 0(csp) # 8-byte Folded Reload + %.fca.0.insert = insertvalue %struct.TwoPointers poison, ptr addrspace(200) %add.ptr, 0 + %.fca.1.insert = insertvalue %struct.TwoPointers %.fca.0.insert, ptr addrspace(200) %add.ptr4, 1 + + ;; CHECK: ct.cret + ret %struct.TwoPointers %.fca.1.insert +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce void @_Z9CheckPtrsv() local_unnamed_addr addrspace(200) #2 { +;; CHECK: _Z9CheckPtrsv: # @_Z9CheckPtrsv +entry: + + ;; CHECK: ct.ccall _Z8InitPtrsv + ;; CHECK-NEXT: ct.ccall _Z7ChgPtrs11TwoPointers + %call = tail call chericcallcce %struct.TwoPointers @_Z8InitPtrsv() + %0 = extractvalue %struct.TwoPointers %call, 0 + %1 = extractvalue %struct.TwoPointers %call, 1 + %call1 = tail call chericcallcce %struct.TwoPointers @_Z7ChgPtrs11TwoPointers(ptr addrspace(200) %0, ptr addrspace(200) %1) + %2 = extractvalue %struct.TwoPointers %call1, 0 + %3 = extractvalue %struct.TwoPointers %call1, 1 + store ptr addrspace(200) %2, ptr addrspace(200) @_Z9CheckPtrsv.x, align 8, !tbaa !11 + store ptr addrspace(200) %3, ptr addrspace(200) getelementptr inbounds nuw (i8, ptr addrspace(200) @_Z9CheckPtrsv.x, i32 8), align 8, !tbaa !11 + ret void +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce %struct.PointerAndInt @_Z10InitPtrIntv() local_unnamed_addr addrspace(200) #1 { + +;; CHECK: _Z10InitPtrIntv: # @_Z10InitPtrIntv +entry: + ;; CHECK: ct.ccall _Z8GetValuev + %call = tail call chericcallcce i32 @_Z8GetValuev() + %rem = urem i32 %call, 5 + + ;; CHECK: auicgp ca1, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset ca1, ca1, %cheriot_compartment_lo_i(.LBB7_1) + ;; CHECK: ct.csetbounds ca1, ca1, %cheriot_compartment_size(dummies) + ;; CHECK: ct.cincoffset cs0, ca1, a0 + %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + + ;; CHECK: ct.ccall _Z8GetValuev + %call1 = tail call chericcallcce i32 @_Z8GetValuev() + + ;; CHECK: mv a1, a0 + ;; CHECK: ct.cmove ca0, cs0 + ;; CHECK: ct.clc cra, 8(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc cs0, 0(csp) # 8-byte Folded Reload + ;; CHECK: ct.cincoffset csp, csp, 16 + %.fca.0.insert = insertvalue %struct.PointerAndInt poison, ptr addrspace(200) %add.ptr, 0 + %.fca.1.insert = insertvalue %struct.PointerAndInt %.fca.0.insert, i32 %call1, 1 + + ;; CHECK: ct.cret + ret %struct.PointerAndInt %.fca.1.insert +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce %struct.PointerAndInt @_Z9ChgPtrInt13PointerAndInt(ptr addrspace(200) %x.coerce0, i32 %x.coerce1) local_unnamed_addr addrspace(200) #1 { +;; CHECK: _Z9ChgPtrInt13PointerAndInt: # @_Z9ChgPtrInt13PointerAndInt +;; CHECK: mv s0, a1 +entry: + + ;; CHECK: .LBB8_1: # %entry + ;; CHECK: # Label of block must be emitted + ;; CHECK: auicgp ca1, %cheriot_compartment_hi(force_use) + ;; CHECK: cincoffset ca1, ca1, %cheriot_compartment_lo_i(.LBB8_1) + ;; CHECK: ct.csc ca0, 0(ca1) + store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + %call = tail call chericcallcce i32 @_Z8GetValuev() + %rem = urem i32 %call, 5 + + ;; CHECK: auicgp ca1, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset ca1, ca1, %cheriot_compartment_lo_i(.LBB8_2) + ;; CHECK: ct.csetbounds ca1, ca1, %cheriot_compartment_size(dummies) + ;; CHECK: ct.cincoffset cs1, ca1, a0 + %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + + ;; CHECK: ct.ccall _Z8GetValuev + %call2 = tail call chericcallcce i32 @_Z8GetValuev() + + ;; CHECK: sub a1, s0, a0 + %sub = sub i32 %x.coerce1, %call2 + + ;; CHECK: ct.cmove ca0, cs1 + ;; CHECK: ct.clc cra, 24(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc cs0, 16(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc cs1, 8(csp) # 8-byte Folded Reload + ;; CHECK: ct.cincoffset csp, csp, 32 + %.fca.0.insert = insertvalue %struct.PointerAndInt poison, ptr addrspace(200) %add.ptr, 0 + %.fca.1.insert = insertvalue %struct.PointerAndInt %.fca.0.insert, i32 %sub, 1 + + ;; CHECK: ct.cret + ret %struct.PointerAndInt %.fca.1.insert +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce void @_Z11CheckPtrIntv() local_unnamed_addr addrspace(200) #2 { +;; CHECK: _Z11CheckPtrIntv: # @_Z11CheckPtrIntv +entry: + + ;; CHECK: ct.ccall _Z10InitPtrIntv + ;; CHECK-NEXT: ct.ccall _Z9ChgPtrInt13PointerAndInt + %call = tail call chericcallcce %struct.PointerAndInt @_Z10InitPtrIntv() + %0 = extractvalue %struct.PointerAndInt %call, 0 + %1 = extractvalue %struct.PointerAndInt %call, 1 + %call1 = tail call chericcallcce %struct.PointerAndInt @_Z9ChgPtrInt13PointerAndInt(ptr addrspace(200) %0, i32 %1) + %2 = extractvalue %struct.PointerAndInt %call1, 0 + %3 = extractvalue %struct.PointerAndInt %call1, 1 + store ptr addrspace(200) %2, ptr addrspace(200) @_Z11CheckPtrIntv.x, align 8, !tbaa !11 + store i32 %3, ptr addrspace(200) getelementptr inbounds nuw (i8, ptr addrspace(200) @_Z11CheckPtrIntv.x, i32 8), align 8, !tbaa !7 + ret void +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce %struct.ParentPtr @_Z13InitParentPtrv() local_unnamed_addr addrspace(200) #1 { + + +;; CHECK: _Z13InitParentPtrv: # @_Z13InitParentPtrv +entry: + + ;; CHECK: ct.ccall _Z8GetValuev + %call = tail call chericcallcce i32 @_Z8GetValuev() + %rem = urem i32 %call, 5 + + ;; CHECK: auicgp cs0, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset cs0, cs0, %cheriot_compartment_lo_i(.LBB10_1) + ;; CHECK: ct.csetbounds cs0, cs0, %cheriot_compartment_size(dummies) + ;; CHECK: ct.cincoffset ca0, cs0, a0 + ;; CHECK: ct.csc ca0, 0(csp) # 8-byte Folded Spill + %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + + ;; CHECK: ct.ccall _Z8GetValuev + %call1 = tail call chericcallcce i32 @_Z8GetValuev() + %rem2 = urem i32 %call1, 5 + + ;; CHECK: ct.cincoffset ca1, cs0, a0 + %add.ptr3 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem2 + + ;; CHECK: ct.clc ca0, 0(csp) # 8-byte Folded Reload + %.fca.0.insert = insertvalue %struct.ParentPtr poison, ptr addrspace(200) %add.ptr, 0 + %.fca.1.0.insert = insertvalue %struct.ParentPtr %.fca.0.insert, ptr addrspace(200) %add.ptr3, 1, 0 + + ;; CHECK: ct.cret + ret %struct.ParentPtr %.fca.1.0.insert +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce %struct.ParentPtr @_Z12ChgParentPtr9ParentPtr(ptr addrspace(200) %x.coerce0, %struct.InnerPtr %x.coerce1) local_unnamed_addr addrspace(200) #1 { + +;; CHECK: _Z12ChgParentPtr9ParentPtr: # @_Z12ChgParentPtr9ParentPtr +;; CHECK: ct.csc ca1, 16(csp) # 8-byte Folded Spill +entry: + + ;; CHECK: auicgp ca1, %cheriot_compartment_hi(force_use) + ;; CHECK: cincoffset ca1, ca1, %cheriot_compartment_lo_i(.LBB11_1) + ;; CHECK: ct.csc ca1, 8(csp) # 8-byte Folded Spill + ;; CHECK: ct.csc ca0, 0(ca1) + %x.coerce1.fca.0.extract = extractvalue %struct.InnerPtr %x.coerce1, 0 + store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + %call = tail call chericcallcce i32 @_Z8GetValuev() + %rem = urem i32 %call, 5 + + ;; CHECK: auicgp cs0, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset cs0, cs0, %cheriot_compartment_lo_i(.LBB11_2) + ;; CHECK: ct.csetbounds cs0, cs0, %cheriot_compartment_size(dummies) + ;; CHECK: ct.cincoffset ca0, cs0, a0 + ;; CHECK: ct.csc ca0, 0(csp) # 8-byte Folded Spill + %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + + ;; CHECK: ct.clc ca0, 16(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc ca1, 8(csp) # 8-byte Folded Reload + ;; CHECK: ct.csc ca0, 0(ca1) + store ptr addrspace(200) %x.coerce1.fca.0.extract, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + %call3 = tail call chericcallcce i32 @_Z8GetValuev() + %rem4 = urem i32 %call3, 5 + + ;; CHECK: ct.cincoffset ca1, cs0, a0 + %add.ptr5 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem4 + + ;; CHECK: ct.clc ca0, 0(csp) # 8-byte Folded Reload + %.fca.0.insert = insertvalue %struct.ParentPtr poison, ptr addrspace(200) %add.ptr, 0 + %.fca.1.0.insert = insertvalue %struct.ParentPtr %.fca.0.insert, ptr addrspace(200) %add.ptr5, 1, 0 + + ;; CHECK: ct.cret + ret %struct.ParentPtr %.fca.1.0.insert +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce void @_Z14CheckParentPtrv() local_unnamed_addr addrspace(200) #2 { + +;; CHECK: _Z14CheckParentPtrv: # @_Z14CheckParentPtrv +entry: + + ;; CHECK: ct.ccall _Z13InitParentPtrv + ;; CHECK-NEXT: ct.ccall _Z12ChgParentPtr9ParentPtr + %call = tail call chericcallcce %struct.ParentPtr @_Z13InitParentPtrv() + %0 = extractvalue %struct.ParentPtr %call, 0 + %1 = extractvalue %struct.ParentPtr %call, 1 + %call1 = tail call chericcallcce %struct.ParentPtr @_Z12ChgParentPtr9ParentPtr(ptr addrspace(200) %0, %struct.InnerPtr %1) + %2 = extractvalue %struct.ParentPtr %call1, 0 + %3 = extractvalue %struct.ParentPtr %call1, 1 + %.fca.0.extract3 = extractvalue %struct.InnerPtr %3, 0 + store ptr addrspace(200) %2, ptr addrspace(200) @_Z14CheckParentPtrv.x, align 8, !tbaa !11 + store ptr addrspace(200) %.fca.0.extract3, ptr addrspace(200) getelementptr inbounds nuw (i8, ptr addrspace(200) @_Z14CheckParentPtrv.x, i32 8), align 8, !tbaa !11 + ret void +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce %struct.TwoPointers @_Z8ChgPtrs2i11TwoPointers(i32 noundef %new_int, ptr addrspace(200) %x.coerce0, ptr addrspace(200) %x.coerce1) local_unnamed_addr addrspace(200) #1 { + +;; CHECK: _Z8ChgPtrs2i11TwoPointers: # @_Z8ChgPtrs2i11TwoPointers +;; CHECK: ct.csc ca2, 32(csp) # 8-byte Folded Spill +;; CHECK: ct.csw a0, 28(csp) # 4-byte Folded Spill +entry: + + ;; CHECK: auicgp ca0, %cheriot_compartment_hi(force_use) + ;; CHECK: cincoffset ca0, ca0, %cheriot_compartment_lo_i(.LBB13_1) + ;; CHECK: ct.csc ca0, 16(csp) # 8-byte Folded Spill + ;; CHECK: ct.csc ca1, 0(ca0) + store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + %call = tail call chericcallcce i32 @_Z8GetValuev() + %add = add i32 %call, %new_int + %rem = urem i32 %add, 5 + + ;; CHECK: auicgp cs0, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset cs0, cs0, %cheriot_compartment_lo_i(.LBB13_2) + ;; CHECK: ct.csetbounds cs0, cs0, %cheriot_compartment_size(dummies) + ;; CHECK: ct.cincoffset ca0, cs0, a0 + ;; CHECK: ct.csc ca0, 8(csp) # 8-byte Folded Spill + %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + + ;; CHECK: ct.clc ca0, 32(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc ca1, 16(csp) # 8-byte Folded Reload + ;; CHECK: ct.csc ca0, 0(ca1) + store ptr addrspace(200) %x.coerce1, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + ;; CHECK: ct.clw a1, 28(csp) # 4-byte Folded Reload + %call2 = tail call chericcallcce i32 @_Z8GetValuev() + %add3 = add i32 %call2, %new_int + %rem4 = urem i32 %add3, 5 + + ;; CHECK: ct.cincoffset ca1, cs0, a0 + %add.ptr5 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem4 + + ;; CHECK: ct.clc ca0, 8(csp) # 8-byte Folded Reload + %.fca.0.insert = insertvalue %struct.TwoPointers poison, ptr addrspace(200) %add.ptr, 0 + %.fca.1.insert = insertvalue %struct.TwoPointers %.fca.0.insert, ptr addrspace(200) %add.ptr5, 1 + + ;; CHECK: ct.cret + ret %struct.TwoPointers %.fca.1.insert +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce %struct.ParentPtr @_Z13ChgParentPtr2i9ParentPtr(i32 noundef %new_int, ptr addrspace(200) %x.coerce0, %struct.InnerPtr %x.coerce1) local_unnamed_addr addrspace(200) #1 { + +;; CHECK: _Z13ChgParentPtr2i9ParentPtr: # @_Z13ChgParentPtr2i9ParentPtr +;; CHECK: ct.csc ca2, 32(csp) # 8-byte Folded Spill +;; CHECK: mv s0, a0 +;; CHECK: ct.csw a0, 28(csp) # 4-byte Folded Spill +entry: + + ;; CHECK: auicgp ca0, %cheriot_compartment_hi(force_use) + ;; CHECK: cincoffset ca0, ca0, %cheriot_compartment_lo_i(.LBB14_1) + ;; CHECK: ct.csc ca0, 16(csp) # 8-byte Folded Spill + ;; CHECK: ct.csc ca1, 0(ca0) + %x.coerce1.fca.0.extract = extractvalue %struct.InnerPtr %x.coerce1, 0 + store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + %call = tail call chericcallcce i32 @_Z8GetValuev() + %add = add i32 %call, %new_int + %rem = urem i32 %add, 5 + + ;; CHECK: auicgp cs0, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset cs0, cs0, %cheriot_compartment_lo_i(.LBB14_2) + ;; CHECK: ct.csetbounds cs0, cs0, %cheriot_compartment_size(dummies) + ;; CHECK: ct.cincoffset ca0, cs0, a0 + ;; CHECK: ct.csc ca0, 8(csp) # 8-byte Folded Spill + %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + + ;; CHECK: ct.clc ca0, 32(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc ca1, 16(csp) # 8-byte Folded Reload + ;; CHECK: ct.csc ca0, 0(ca1) + store ptr addrspace(200) %x.coerce1.fca.0.extract, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + ;; CHECK: ct.clw a1, 28(csp) # 4-byte Folded Reload + %call3 = tail call chericcallcce i32 @_Z8GetValuev() + %add4 = add i32 %call3, %new_int + %rem5 = urem i32 %add4, 5 + + ;; CHECK: ct.cincoffset ca1, cs0, a0 + %add.ptr6 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem5 + + ;; CHECK: ct.clc ca0, 8(csp) # 8-byte Folded Reload + %.fca.0.insert = insertvalue %struct.ParentPtr poison, ptr addrspace(200) %add.ptr, 0 + %.fca.1.0.insert = insertvalue %struct.ParentPtr %.fca.0.insert, ptr addrspace(200) %add.ptr6, 1, 0 + + ;; CHECK: ct.cret + ret %struct.ParentPtr %.fca.1.0.insert +} + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn +declare void @llvm.va_start.p200(ptr addrspace(200)) addrspace(200) #6 + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn +declare void @llvm.va_end.p200(ptr addrspace(200)) addrspace(200) #6 + +; Function Attrs: nofree noinline norecurse nounwind +define dso_local chericcallcce %struct.TwoPointers @_Z8ChgPtrs3i11TwoPointersz(i32 noundef %n, ptr addrspace(200) %x.coerce0, ptr addrspace(200) %x.coerce1, ...) local_unnamed_addr addrspace(200) #7 { + +;; CHECK: _Z8ChgPtrs3i11TwoPointersz: # @_Z8ChgPtrs3i11TwoPointersz +entry: + %args = alloca ptr addrspace(200), align 8, addrspace(200) + %_ = alloca i32, align 4, addrspace(200) + call void @llvm.lifetime.start.p200(i64 8, ptr addrspace(200) nonnull %args) #8 + call void @llvm.va_start.p200(ptr addrspace(200) nonnull %args) + store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + call void @llvm.lifetime.start.p200(i64 4, ptr addrspace(200) nonnull %_) + %call = call chericcallcce i32 @_Z8GetValuev() + store volatile i32 %call, ptr addrspace(200) %_, align 4, !tbaa !7 + store ptr addrspace(200) %x.coerce1, ptr addrspace(200) @force_use, align 8, !tbaa !11 + %cmp10 = icmp sgt i32 %n, 0 + br i1 %cmp10, label %for.body, label %for.cond.cleanup + +for.cond.for.cond.cleanup_crit_edge: ; preds = %for.body + ;; CHECK: auicgp ca2, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset ca2, ca2, %cheriot_compartment_lo_i(.LBB15_5) + ;; CHECK: ct.csetbounds ca2, ca2, %cheriot_compartment_size(dummies) + + ;; CHECK: ct.cincoffset ca3, ca2, s1 + %add.le = add i32 %call1, %0 + %rem.le = urem i32 %add.le, 5 + %add.ptr.le = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem.le + + ;; CHECK: ct.cincoffset ca1, ca2, a0 + %add4.le = add i32 %call3, %0 + %rem5.le = urem i32 %add4.le, 5 + %add.ptr6.le = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem5.le + + br label %for.cond.cleanup + +for.body: ; preds = %entry, %for.body + %i.011 = phi i32 [ %inc, %for.body ], [ 0, %entry ] + %argp.cur = load ptr addrspace(200), ptr addrspace(200) %args, align 8 + %argp.next = getelementptr inbounds nuw i8, ptr addrspace(200) %argp.cur, i32 4 + store ptr addrspace(200) %argp.next, ptr addrspace(200) %args, align 8 + %0 = load i32, ptr addrspace(200) %argp.cur, align 4, !tbaa !7 + %call1 = call chericcallcce i32 @_Z8GetValuev() + %call3 = call chericcallcce i32 @_Z8GetValuev() + %inc = add nuw nsw i32 %i.011, 1 + %exitcond.not = icmp eq i32 %inc, %n + br i1 %exitcond.not, label %for.cond.for.cond.cleanup_crit_edge, label %for.body, !llvm.loop !17 + +for.cond.cleanup: ; preds = %for.cond.for.cond.cleanup_crit_edge, %entry + %x.sroa.0.0.lcssa = phi ptr addrspace(200) [ %add.ptr.le, %for.cond.for.cond.cleanup_crit_edge ], [ %x.coerce0, %entry ] + %x.sroa.4.0.lcssa = phi ptr addrspace(200) [ %add.ptr6.le, %for.cond.for.cond.cleanup_crit_edge ], [ %x.coerce1, %entry ] + call void @llvm.va_end.p200(ptr addrspace(200) %args) + call void @llvm.lifetime.end.p200(i64 4, ptr addrspace(200) nonnull %_) + call void @llvm.lifetime.end.p200(i64 8, ptr addrspace(200) nonnull %args) #8 + + ;; CHECK: ct.cmove ca0, ca3 + %.fca.0.insert = insertvalue %struct.TwoPointers poison, ptr addrspace(200) %x.sroa.0.0.lcssa, 0 + %.fca.1.insert = insertvalue %struct.TwoPointers %.fca.0.insert, ptr addrspace(200) %x.sroa.4.0.lcssa, 1 + + ;; CHECK: ct.cret + ret %struct.TwoPointers %.fca.1.insert + +} + +; Function Attrs: nofree noinline norecurse nounwind +define dso_local chericcallcce %struct.ParentPtr @_Z13ChgParentPtr3i9ParentPtrz(i32 noundef %n, ptr addrspace(200) %x.coerce0, %struct.InnerPtr %x.coerce1, ...) local_unnamed_addr addrspace(200) #7 { + +;; CHECK: _Z13ChgParentPtr3i9ParentPtrz: # @_Z13ChgParentPtr3i9ParentPtrz +entry: + %args = alloca ptr addrspace(200), align 8, addrspace(200) + %_ = alloca i32, align 4, addrspace(200) + %x.coerce1.fca.0.extract = extractvalue %struct.InnerPtr %x.coerce1, 0 + call void @llvm.lifetime.start.p200(i64 8, ptr addrspace(200) nonnull %args) #8 + call void @llvm.va_start.p200(ptr addrspace(200) nonnull %args) + store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + call void @llvm.lifetime.start.p200(i64 4, ptr addrspace(200) nonnull %_) + %call = call chericcallcce i32 @_Z8GetValuev() + store volatile i32 %call, ptr addrspace(200) %_, align 4, !tbaa !7 + store ptr addrspace(200) %x.coerce1.fca.0.extract, ptr addrspace(200) @force_use, align 8, !tbaa !11 + %cmp12 = icmp sgt i32 %n, 0 + br i1 %cmp12, label %for.body, label %for.cond.cleanup + +for.cond.for.cond.cleanup_crit_edge: ; preds = %for.body + %add.le = add i32 %call2, %0 + %rem.le = urem i32 %add.le, 5 + %add.ptr.le = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem.le + %add5.le = add i32 %call4, %0 + %rem6.le = urem i32 %add5.le, 5 + %add.ptr7.le = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem6.le + ;; CHECK: auicgp ca2, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset ca2, ca2, %cheriot_compartment_lo_i(.LBB16_6) + ;; CHECK: ct.csetbounds ca2, ca2, %cheriot_compartment_size(dummies) + ;; CHECK: ct.cincoffset ca0, ca2, s1 + ;; CHECK: ct.cincoffset ca1, ca2, a1 + ;; CHECK: j .LBB16_4 + br label %for.cond.cleanup + +for.body: ; preds = %entry, %for.body + %i.013 = phi i32 [ %inc, %for.body ], [ 0, %entry ] + %argp.cur = load ptr addrspace(200), ptr addrspace(200) %args, align 8 + %argp.next = getelementptr inbounds nuw i8, ptr addrspace(200) %argp.cur, i32 4 + store ptr addrspace(200) %argp.next, ptr addrspace(200) %args, align 8 + %0 = load i32, ptr addrspace(200) %argp.cur, align 4, !tbaa !7 + %call2 = call chericcallcce i32 @_Z8GetValuev() + %call4 = call chericcallcce i32 @_Z8GetValuev() + %inc = add nuw nsw i32 %i.013, 1 + %exitcond.not = icmp eq i32 %inc, %n + br i1 %exitcond.not, label %for.cond.for.cond.cleanup_crit_edge, label %for.body, !llvm.loop !18 + +for.cond.cleanup: ; preds = %for.cond.for.cond.cleanup_crit_edge, %entry + %x.sroa.0.0.lcssa = phi ptr addrspace(200) [ %add.ptr.le, %for.cond.for.cond.cleanup_crit_edge ], [ %x.coerce0, %entry ] + %x.sroa.4.0.lcssa = phi ptr addrspace(200) [ %add.ptr7.le, %for.cond.for.cond.cleanup_crit_edge ], [ %x.coerce1.fca.0.extract, %entry ] + call void @llvm.va_end.p200(ptr addrspace(200) %args) + call void @llvm.lifetime.end.p200(i64 4, ptr addrspace(200) nonnull %_) + call void @llvm.lifetime.end.p200(i64 8, ptr addrspace(200) nonnull %args) #8 + %.fca.0.insert = insertvalue %struct.ParentPtr poison, ptr addrspace(200) %x.sroa.0.0.lcssa, 0 + %.fca.1.0.insert = insertvalue %struct.ParentPtr %.fca.0.insert, ptr addrspace(200) %x.sroa.4.0.lcssa, 1, 0 + + ;; CHECK: .LBB16_4: # %for.cond.cleanup + ;; CHECK: ct.clc cra, 56(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc cs0, 48(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc cs1, 40(csp) # 8-byte Folded Reload + ;; CHECK: ct.cincoffset csp, csp, 64 + ;; CHECK: ct.cret + ret %struct.ParentPtr %.fca.1.0.insert + +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce %struct.TwoPointers @_Z8ChgPtrs4iiiii11TwoPointers(i32 noundef %n0, i32 noundef %n1, i32 noundef %n2, i32 noundef %n3, i32 noundef %n4, ptr addrspace(200) %x.coerce0, ptr addrspace(200) %x.coerce1) local_unnamed_addr addrspace(200) #1 { +;; CHECK: _Z8ChgPtrs4iiiii11TwoPointers: # @_Z8ChgPtrs4iiiii11TwoPointers +;; CHECK: mv s1, a1 +;; CHECK: mv s0, a0 +;; CHECK: ct.clc ca0, 0(ct0) +;; CHECK: ct.csc ca0, 32(csp) # 8-byte Folded Spill +entry: + ;; CHECK: auicgp ca0, %cheriot_compartment_hi(force_use) + ;; CHECK: cincoffset ca0, ca0, %cheriot_compartment_lo_i(.LBB17_2) + ;; CHECK: ct.csc ca0, 24(csp) # 8-byte Folded Spill + ;; CHECK: ct.csc ca5, 0(ca0) + store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + %call = tail call chericcallcce i32 @_Z8GetValuev() + + %add = add i32 %n1, %n0 + %add1 = add i32 %add, %n2 + %add2 = add i32 %add1, %n3 + %add3 = add i32 %add2, %n4 + %add4 = add i32 %add3, %call + %rem = urem i32 %add4, 5 + + ;; CHECK: auicgp ca3, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset ca3, ca3, %cheriot_compartment_lo_i(.LBB17_3) + ;; CHECK: ct.csc ca3, 8(csp) # 8-byte Folded Spill + ;; CHECK: ct.cincoffset ca0, ca3, a0 + ;; CHECK: ct.csc ca0, 16(csp) # 8-byte Folded Spill + %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + + ;; CHECK: ct.clc ca0, 32(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc ca1, 24(csp) # 8-byte Folded Reload + store ptr addrspace(200) %x.coerce1, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + %call6 = tail call chericcallcce i32 @_Z8GetValuev() + %add7 = add i32 %n1, %n0 + %add8 = add i32 %add7, %n2 + %add9 = add i32 %add8, %n3 + %add10 = add i32 %add9, %n4 + %add11 = add i32 %add10, %call6 + %rem12 = urem i32 %add11, 5 + + ;; CHECK: ct.clc ca1, 8(csp) # 8-byte Folded Reload + ;; CHECK: ct.cincoffset ca1, ca1, a0 + %add.ptr13 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem12 + + ;; CHECK: ct.clc ca0, 16(csp) # 8-byte Folded Reload + %.fca.0.insert = insertvalue %struct.TwoPointers poison, ptr addrspace(200) %add.ptr, 0 + %.fca.1.insert = insertvalue %struct.TwoPointers %.fca.0.insert, ptr addrspace(200) %add.ptr13, 1 + + ;; CHECK: ct.cret + ret %struct.TwoPointers %.fca.1.insert +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define dso_local chericcallcce %struct.ParentPtr @_Z13ChgParentPtr4iiiii9ParentPtr(i32 noundef %n0, i32 noundef %n1, i32 noundef %n2, i32 noundef %n3, i32 noundef %n4, ptr addrspace(200) %x.coerce0, %struct.InnerPtr %x.coerce1) local_unnamed_addr addrspace(200) #1 { + +;; CHECK: _Z13ChgParentPtr4iiiii9ParentPtr: # @_Z13ChgParentPtr4iiiii9ParentPtr +;; CHECK: mv s1, a1 +;; CHECK: mv s0, a0 +;; CHECK: ct.clc ca0, 0(ct0) +;; CHECK: ct.csc ca0, 32(csp) # 8-byte Folded Spill +entry: + + ;; CHECK: auicgp ca0, %cheriot_compartment_hi(force_use) + ;; CHECK: cincoffset ca0, ca0, %cheriot_compartment_lo_i(.LBB18_2) + ;; CHECK: ct.csc ca0, 24(csp) # 8-byte Folded Spill + ;; CHECK: ct.csc ca5, 0(ca0) + %x.coerce1.fca.0.extract = extractvalue %struct.InnerPtr %x.coerce1, 0 + store ptr addrspace(200) %x.coerce0, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + %call = tail call chericcallcce i32 @_Z8GetValuev() + %add = add i32 %n1, %n0 + %add2 = add i32 %add, %n2 + %add3 = add i32 %add2, %n3 + %add4 = add i32 %add3, %n4 + %add5 = add i32 %add4, %call + %rem = urem i32 %add5, 5 + + ;; CHECK: auicgp ca3, %cheriot_compartment_hi(dummies) + ;; CHECK: cincoffset ca3, ca3, %cheriot_compartment_lo_i(.LBB18_3) + ;; CHECK: ct.csetbounds ca3, ca3, %cheriot_compartment_size(dummies) + ;; CHECK: ct.csc ca3, 8(csp) # 8-byte Folded Spill + ;; CHECK: ct.cincoffset ca0, ca3, a0 + ;; CHECK: ct.csc ca0, 16(csp) # 8-byte Folded Spill + %add.ptr = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem + + ;; CHECK: ct.clc ca0, 32(csp) # 8-byte Folded Reload + ;; CHECK: ct.clc ca1, 24(csp) # 8-byte Folded Reload + store ptr addrspace(200) %x.coerce1.fca.0.extract, ptr addrspace(200) @force_use, align 8, !tbaa !11 + + ;; CHECK: ct.ccall _Z8GetValuev + %call7 = tail call chericcallcce i32 @_Z8GetValuev() + %add8 = add i32 %n1, %n0 + %add9 = add i32 %add8, %n2 + %add10 = add i32 %add9, %n3 + %add11 = add i32 %add10, %n4 + %add12 = add i32 %add11, %call7 + %rem13 = urem i32 %add12, 5 + + ;; CHECK: ct.cincoffset ca1, ca1, a0 + %add.ptr14 = getelementptr inbounds nuw i32, ptr addrspace(200) @dummies, i32 %rem13 + + ;; CHECK: ct.clc ca0, 16(csp) # 8-byte Folded Reload + %.fca.0.insert = insertvalue %struct.ParentPtr poison, ptr addrspace(200) %add.ptr, 0 + %.fca.1.0.insert = insertvalue %struct.ParentPtr %.fca.0.insert, ptr addrspace(200) %add.ptr14, 1, 0 + + ;; CHECK: ct.cret + ret %struct.ParentPtr %.fca.1.0.insert +} + +attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, argmem: none, inaccessiblemem: none) "cheri-compartment"="example" "interrupt-state"="enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+32bit,+c,+cap-mode,+e,+m,+xcheri" } +attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) "cheri-compartment"="example" "interrupt-state"="enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+32bit,+c,+cap-mode,+e,+m,+xcheri" } +attributes #2 = { mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) "cheri-compartment"="example" "interrupt-state"="enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+32bit,+c,+cap-mode,+e,+m,+xcheri" } +attributes #3 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #4 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) "cheri-compartment"="example" "interrupt-state"="enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+32bit,+c,+cap-mode,+e,+m,+xcheri" } +attributes #5 = { nofree noinline norecurse nosync nounwind "cheri-compartment"="example" "interrupt-state"="enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+32bit,+c,+cap-mode,+e,+m,+xcheri" } +attributes #6 = { mustprogress nocallback nofree nosync nounwind willreturn } +attributes #7 = { nofree noinline norecurse nounwind "cheri-compartment"="example" "interrupt-state"="enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+32bit,+c,+cap-mode,+e,+m,+xcheri" } +attributes #8 = { nounwind } + +!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 1, !"Code Model", i32 1} +!5 = !{i32 8, !"SmallDataLimit", i32 0} +!6 = !{!"clang version 20.1.3"} +!7 = !{!8, !8, i64 0} +!8 = !{!"int", !9, i64 0} +!9 = !{!"omnipotent char", !10, i64 0} +!10 = !{!"Simple C/C++ TBAA"} +!11 = !{!12, !12, i64 0} +!12 = !{!"p1 int", !13, i64 0} +!13 = !{!"any pointer", !9, i64 0} +!14 = distinct !{!14, !15, !16} +!15 = !{!"llvm.loop.mustprogress"} +!16 = !{!"llvm.loop.unroll.disable"} +!17 = distinct !{!17, !15, !16} +!18 = distinct !{!18, !15, !16}