diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 7102b2da91d0..bf969c392d2b 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -244,6 +244,19 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return createCast(mlir::cir::CastKind::int_to_ptr, src, newTy); } + mlir::Value createGetMemberOp(mlir::Location &loc, mlir::Value structPtr, + const char *fldName, unsigned idx) { + + assert(structPtr.getType().isa()); + auto structBaseTy = + structPtr.getType().cast().getPointee(); + assert(structBaseTy.isa()); + auto fldTy = structBaseTy.cast().getMembers()[idx]; + auto fldPtrTy = ::mlir::cir::PointerType::get(getContext(), fldTy); + return create(loc, fldPtrTy, structPtr, fldName, + idx); + } + mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) { return createCast(mlir::cir::CastKind::ptr_to_int, src, newTy); } diff --git a/clang/lib/CIR/Dialect/IR/MissingFeatures.h b/clang/lib/CIR/Dialect/IR/MissingFeatures.h index e21fc0e0b191..f33f1072f610 100644 --- a/clang/lib/CIR/Dialect/IR/MissingFeatures.h +++ b/clang/lib/CIR/Dialect/IR/MissingFeatures.h @@ -21,6 +21,7 @@ struct MissingFeatures { // C++ ABI support static bool cxxABI() { return false; } static bool setCallingConv() { return false; } + static bool handleBigEndian() { return false; } // Address space related static bool addressSpace() { return false; } diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt index 8bd6a06b7c4e..0f7521b17327 100644 --- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt @@ -2,6 +2,7 @@ add_clang_library(MLIRCIRTransforms LifetimeCheck.cpp LoweringPrepare.cpp LoweringPrepareItaniumCXXABI.cpp + LoweringPrepareAArch64CXXABI.cpp MergeCleanups.cpp DropAST.cpp IdiomRecognizer.cpp diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index e92e40b7ccd4..5b596f9612be 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -70,6 +70,7 @@ struct LoweringPreparePass : public LoweringPrepareBase { void runOnOp(Operation *op); void lowerThreeWayCmpOp(CmpThreeWayOp op); + void lowerVAArgOp(VAArgOp op); void lowerGlobalOp(GlobalOp op); void lowerDynamicCastOp(DynamicCastOp op); void lowerStdFindOp(StdFindOp op); @@ -108,13 +109,14 @@ struct LoweringPreparePass : public LoweringPrepareBase { void setASTContext(clang::ASTContext *c) { astCtx = c; - switch (c->getCXXABIKind()) { + auto kind = c->getCXXABIKind(); + switch (kind) { case clang::TargetCXXABI::GenericItanium: + cxxABI.reset(::cir::LoweringPrepareCXXABI::createItaniumABI()); + break; case clang::TargetCXXABI::GenericAArch64: case clang::TargetCXXABI::AppleARM64: - // TODO: this isn't quite right, clang uses AppleARM64CXXABI which - // inherits from ARMCXXABI. We'll have to follow suit. - cxxABI.reset(::cir::LoweringPrepareCXXABI::createItaniumABI()); + cxxABI.reset(::cir::LoweringPrepareCXXABI::createAArch64ABI(kind)); break; default: @@ -320,6 +322,18 @@ static void canonicalizeIntrinsicThreeWayCmp(CIRBaseBuilderTy &builder, op.erase(); } +void LoweringPreparePass::lowerVAArgOp(VAArgOp op) { + CIRBaseBuilderTy builder(getContext()); + builder.setInsertionPoint(op); + + auto res = cxxABI->lowerVAArg(builder, op); + if (res) { + op.replaceAllUsesWith(res); + op.erase(); + } + return; +} + void LoweringPreparePass::lowerThreeWayCmpOp(CmpThreeWayOp op) { CIRBaseBuilderTy builder(getContext()); builder.setInsertionPointAfter(op); @@ -601,6 +615,8 @@ void LoweringPreparePass::lowerIterEndOp(IterEndOp op) { void LoweringPreparePass::runOnOp(Operation *op) { if (auto threeWayCmp = dyn_cast(op)) { lowerThreeWayCmpOp(threeWayCmp); + } else if (auto vaArgOp = dyn_cast(op)) { + lowerVAArgOp(vaArgOp); } else if (auto getGlobal = dyn_cast(op)) { lowerGlobalOp(getGlobal); } else if (auto dynamicCast = dyn_cast(op)) { @@ -633,8 +649,9 @@ void LoweringPreparePass::runOnOperation() { SmallVector opsToTransform; op->walk([&](Operation *op) { - if (isa(op)) + if (isa( + op)) opsToTransform.push_back(op); }); diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepareAArch64CXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareAArch64CXXABI.cpp new file mode 100644 index 000000000000..24df34c39e43 --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareAArch64CXXABI.cpp @@ -0,0 +1,176 @@ +//====- LoweringPrepareArm64CXXABI.cpp - Arm64 ABI specific code --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides ARM64 C++ ABI specific code that is used during LLVMIR +// lowering prepare. +// +//===----------------------------------------------------------------------===// + +#include "../IR/MissingFeatures.h" +#include "LoweringPrepareItaniumCXXABI.h" + +#include + +using cir::LoweringPrepareCXXABI; +using cir::MissingFeatures; + +using namespace mlir; +using namespace mlir::cir; + +namespace { +class LoweringPrepareAArch64CXXABI : public LoweringPrepareItaniumCXXABI { +public: + LoweringPrepareAArch64CXXABI(clang::TargetCXXABI::Kind k) : Kind(k) {} + mlir::Value lowerVAArg(CIRBaseBuilderTy &builder, + mlir::cir::VAArgOp op) override; + +private: + clang::TargetCXXABI::Kind Kind; + mlir::Value lowerGenericAArch64VAArg(CIRBaseBuilderTy &builder, + mlir::cir::VAArgOp op); +}; +} // namespace + +LoweringPrepareCXXABI * +LoweringPrepareCXXABI::createAArch64ABI(clang::TargetCXXABI::Kind k) { + return new LoweringPrepareAArch64CXXABI(k); +} + +mlir::Value LoweringPrepareAArch64CXXABI::lowerGenericAArch64VAArg( + CIRBaseBuilderTy &builder, mlir::cir::VAArgOp op) { + auto loc = op->getLoc(); + auto valist = op->getOperand(0); + auto opResTy = op.getType(); + // front end should not produce non-scalar type of VAArgOp + bool isSupportedType = + opResTy.isa(); + assert(isSupportedType); + + assert(!MissingFeatures::handleBigEndian()); + + bool isFloatingType = + opResTy.isa(); + + // The AArch64 va_list type and handling is specified in the Procedure Call + // Standard, section B.4: + // + // struct { + // void *__stack; + // void *__gr_top; + // void *__vr_top; + // int __gr_offs; + // int __vr_offs; + // }; + auto curInsertionP = builder.saveInsertionPoint(); + auto currentBlock = builder.getInsertionBlock(); + auto boolTy = builder.getBoolTy(); + + auto maybeRegBlock = builder.createBlock(builder.getBlock()->getParent()); + auto inRegBlock = builder.createBlock(builder.getBlock()->getParent()); + auto onStackBlock = builder.createBlock(builder.getBlock()->getParent()); + + //======================================= + // Find out where argument was passed + //======================================= + + // If v/gr_offs >= 0 we're already using the stack for this type of + // argument. We don't want to keep updating reg_offs (in case it overflows, + // though anyone passing 2GB of arguments, each at most 16 bytes, deserves + // whatever they get). + builder.restoreInsertionPoint(curInsertionP); + // 3 is the field number of __gr_offs, 4 is the field number of __vr_offs + auto offsP = builder.createGetMemberOp(loc, valist, + isFloatingType ? "vr_offs" : "gr_offs", + isFloatingType ? 4 : 3); + auto offs = builder.create(loc, offsP); + auto zeroValue = builder.create( + loc, offs.getType(), mlir::cir::IntAttr::get(offs.getType(), 0)); + auto cmpRes = builder.create(loc, boolTy, CmpOpKind::ge, + offs, zeroValue); + builder.create(loc, cmpRes, onStackBlock, maybeRegBlock); + auto newEndBlock = currentBlock->splitBlock(op); + + // maybeRegBlock updates the gr_offs/vr_offs pointer for next call to va_arg + // on this va_list. The fact that this is done unconditionally reflects the + // fact that allocating an argument to the stack also uses up all the + // remaining registers of the appropriate kind. + builder.setInsertionPointToEnd(maybeRegBlock); + auto boundaryValue = builder.create( + loc, offs.getType(), + mlir::cir::IntAttr::get(offs.getType(), isFloatingType ? 16 : 8)); + auto newRegsOffs = builder.create( + loc, offs.getType(), mlir::cir::BinOpKind::Add, offs, boundaryValue); + builder.createStore(loc, newRegsOffs, offsP); + // Now we're in a position to decide whether this argument really was in + // registers or not. + auto maybeRegCmpRes = builder.create( + loc, boolTy, CmpOpKind::le, newRegsOffs, zeroValue); + builder.create(loc, maybeRegCmpRes, inRegBlock, + onStackBlock); + + //======================================= + // Argument was on the stack + //======================================= + builder.setInsertionPointToEnd(onStackBlock); + auto stackP = builder.createGetMemberOp(loc, valist, "stack", 0); + auto stack = builder.create(loc, stackP); + auto ptrDiffTy = + mlir::cir::IntType::get(builder.getContext(), 64, /*signed=*/false); + auto eight = builder.create( + loc, ptrDiffTy, mlir::cir::IntAttr::get(ptrDiffTy, 8)); + auto i8Ty = IntegerType::get(builder.getContext(), 8); + auto i8PtrTy = PointerType::get(builder.getContext(), i8Ty); + auto castStack = builder.createBitcast(stack, i8PtrTy); + // Write the new value of __stack for the next call to va_arg + auto newStackAsi8Ptr = builder.create( + loc, castStack.getType(), castStack, eight); + auto newStack = builder.createBitcast(newStackAsi8Ptr, stack.getType()); + builder.createStore(loc, newStack, stackP); + builder.create(loc, mlir::ValueRange{stack}, newEndBlock); + + //======================================= + // Argument was in registers + //======================================= + // Now we emit the code for if the argument was originally passed in + // registers. First start the appropriate block: + builder.setInsertionPointToEnd(inRegBlock); + auto regTopP = builder.createGetMemberOp(loc, valist, + isFloatingType ? "vr_top" : "gr_top", + isFloatingType ? 2 : 1); + auto regTop = builder.create(loc, regTopP); + auto castRegTop = builder.createBitcast(regTop, i8PtrTy); + auto resAsInt8P = builder.create( + loc, castRegTop.getType(), castRegTop, offs); + auto resAsVoidP = builder.createBitcast(resAsInt8P, regTop.getType()); + builder.create(loc, mlir::ValueRange{resAsVoidP}, + newEndBlock); + + // generate additional instructions for end block + builder.setInsertionPoint(op); + newEndBlock->addArgument(stack.getType(), loc); + auto resP = newEndBlock->getArgument(0); + assert(resP.getType().isa()); + auto opResPTy = PointerType::get(builder.getContext(), opResTy); + auto castResP = builder.createBitcast(resP, opResPTy); + auto res = builder.create(loc, castResP); + return res.getResult(); +} + +mlir::Value LoweringPrepareAArch64CXXABI::lowerVAArg(CIRBaseBuilderTy &builder, + mlir::cir::VAArgOp op) { + + if (Kind == clang::TargetCXXABI::GenericAArch64) { + return lowerGenericAArch64VAArg(builder, op); + } + // Return empty value here so CIR Lowering don't do anything. + // This would leave the responsibility to LLVM Lowering to handle cir.va_arg + // which could be better if algorithm is simple. + return mlir::Value(); +} diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h index 2a094bad8702..3368d19d04fb 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h @@ -24,10 +24,13 @@ namespace cir { class LoweringPrepareCXXABI { public: static LoweringPrepareCXXABI *createItaniumABI(); + static LoweringPrepareCXXABI *createAArch64ABI(clang::TargetCXXABI::Kind k); virtual mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder, mlir::cir::DynamicCastOp op) = 0; + virtual mlir::Value lowerVAArg(CIRBaseBuilderTy &builder, + mlir::cir::VAArgOp op) = 0; virtual ~LoweringPrepareCXXABI() {} }; diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp index 3619648056cc..07d4329d8500 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp @@ -1,4 +1,5 @@ -//====- LoweringPrepareItaniumCXXABI.h - Itanium ABI specific code --------===// +//====- LoweringPrepareItaniumCXXABI.cpp - Itanium ABI specific code +//--------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,8 +12,8 @@ // //===----------------------------------------------------------------------===// +#include "LoweringPrepareItaniumCXXABI.h" #include "../IR/MissingFeatures.h" -#include "LoweringPrepareCXXABI.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/Value.h" #include "mlir/IR/ValueRange.h" @@ -20,17 +21,11 @@ #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" -using namespace cir; - -namespace { - -class LoweringPrepareItaniumCXXABI : public LoweringPrepareCXXABI { -public: - mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder, - mlir::cir::DynamicCastOp op) override; -}; - -} // namespace +using cir::CIRBaseBuilderTy; +using cir::LoweringPrepareCXXABI; +using cir::MissingFeatures; +using namespace mlir; +using namespace mlir::cir; LoweringPrepareCXXABI *LoweringPrepareCXXABI::createItaniumABI() { return new LoweringPrepareItaniumCXXABI(); @@ -115,3 +110,9 @@ LoweringPrepareItaniumCXXABI::lowerDynamicCast(CIRBaseBuilderTy &builder, }) .getResult(); } + +mlir::Value LoweringPrepareItaniumCXXABI::lowerVAArg(CIRBaseBuilderTy &builder, + mlir::cir::VAArgOp op) { + // TODO: implement va_arg for more generic Itanium ABI. + return mlir::Value(); +} diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.h b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.h new file mode 100644 index 000000000000..d6f5dac033cc --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.h @@ -0,0 +1,24 @@ +//====- LoweringPrepareItaniumCXXABI.h - Itanium ABI specific code --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides Itanium C++ ABI specific code that is used during LLVMIR +// lowering prepare. +// +//===----------------------------------------------------------------------===// + +#include "LoweringPrepareCXXABI.h" + +using cir::CIRBaseBuilderTy; + +class LoweringPrepareItaniumCXXABI : public cir::LoweringPrepareCXXABI { +public: + mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder, + mlir::cir::DynamicCastOp op) override; + mlir::Value lowerVAArg(CIRBaseBuilderTy &builder, + mlir::cir::VAArgOp op) override; +}; diff --git a/clang/test/CIR/CodeGen/var-arg-float.c b/clang/test/CIR/CodeGen/var-arg-float.c new file mode 100644 index 000000000000..6935e9692578 --- /dev/null +++ b/clang/test/CIR/CodeGen/var-arg-float.c @@ -0,0 +1,108 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=BEFORE +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=AFTER +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM +#include + +double f1(int n, ...) { + va_list valist; + va_start(valist, n); + double res = va_arg(valist, double); + va_end(valist); + return res; +} + +// BEFORE: !ty_22__va_list22 = !cir.struct, !cir.ptr, !cir.ptr, !cir.int, !cir.int} +// BEFORE: cir.func @f1(%arg0: !s32i, ...) -> !cir.double +// BEFORE: [[RETP:%.*]] = cir.alloca !cir.double, !cir.ptr, ["__retval"] +// BEFORE: [[RESP:%.*]] = cir.alloca !cir.double, !cir.ptr, ["res", init] +// BEFORE: cir.va.start [[VARLIST:%.*]] : !cir.ptr +// BEFORE: [[TMP0:%.*]] = cir.va.arg [[VARLIST]] : (!cir.ptr) -> !cir.double +// BEFORE: cir.store [[TMP0]], [[RESP]] : !cir.double, !cir.ptr +// BEFORE: cir.va.end [[VARLIST]] : !cir.ptr +// BEFORE: [[RES:%.*]] = cir.load [[RESP]] : !cir.ptr, !cir.double +// BEFORE: cir.store [[RES]], [[RETP]] : !cir.double, !cir.ptr +// BEFORE: [[RETV:%.*]] = cir.load [[RETP]] : !cir.ptr, !cir.double +// BEFORE: cir.return [[RETV]] : !cir.double + +// AFTER: !ty_22__va_list22 = !cir.struct, !cir.ptr, !cir.ptr, !cir.int, !cir.int} +// LLVM: %struct.__va_list = type { ptr, ptr, ptr, i32, i32 } +// AFTER: cir.func @f1(%arg0: !s32i, ...) -> !cir.double +// LLVM: define double @f1(i32 %0, ...) +// LLVM: [[ARGN:%.*]] = alloca i32, i64 1, align 4, +// AFTER: [[RETP:%.*]] = cir.alloca !cir.double, !cir.ptr, ["__retval"] +// LLVM: [[RETP:%.*]] = alloca double, i64 1, align 8, +// AFTER: [[RESP:%.*]] = cir.alloca !cir.double, !cir.ptr, ["res", init] +// LLVM: [[RESP:%.*]] = alloca double, i64 1, align 8, +// AFTER: cir.va.start [[VARLIST:%.*]] : !cir.ptr +// LLVM: call void @llvm.va_start.p0(ptr [[VARLIST:%.*]]), +// AFTER: [[VR_OFFS_P:%.*]] = cir.get_member [[VARLIST]][4] {name = "vr_offs"} : !cir.ptr -> !cir.ptr +// LLVM: [[VR_OFFS_P:%.*]] = getelementptr %struct.__va_list, ptr [[VARLIST]], i32 0, i32 4 +// AFTER: [[VR_OFFS:%.*]] = cir.load [[VR_OFFS_P]] : !cir.ptr, !s32i +// LLVM: [[VR_OFFS:%.*]] = load i32, ptr [[VR_OFFS_P]], align 4, +// AFTER: [[ZERO:%.*]] = cir.const #cir.int<0> : !s32i +// AFTER: [[CMP0:%.*]] = cir.cmp(ge, [[VR_OFFS]], [[ZERO]]) : !s32i, !cir.bool +// LLVM-NEXT: [[CMP0:%.*]] = icmp sge i32 [[VR_OFFS]], 0, +// AFTER-NEXT: cir.brcond [[CMP0]] [[BB_ON_STACK:\^bb.*]], [[BB_MAY_REG:\^bb.*]] +// LLVM-NEXT: br i1 [[CMP0]], label %[[BB_ON_STACK:.*]], label %[[BB_MAY_REG:.*]], + +// AFTER-NEXT: [[BB_END:\^bb.*]]([[BLK_ARG:%.*]]: !cir.ptr): // 2 preds: [[BB_IN_REG:\^bb.*]], [[BB_ON_STACK]] +// LLVM: [[BB_END:.*]]: ; preds = %[[BB_ON_STACK]], %[[BB_IN_REG:.*]] +// AFTER-NEXT: [[TMP0:%.*]] = cir.cast(bitcast, [[BLK_ARG]] : !cir.ptr), !cir.ptr +// LLVM-NEXT: [[PHIP:%.*]] = phi ptr [ [[IN_REG_OUTPUT:%.*]], %[[BB_IN_REG]] ], [ [[STACK_V:%.*]], %[[BB_ON_STACK]] ] +// AFTER-NEXT: [[TMP1:%.*]] = cir.load [[TMP0]] : !cir.ptr, !cir.double +// LLVM-NEXT: [[PHIV:%.*]] = load double, ptr [[PHIP]], align 8, +// AFTER: cir.store [[TMP1]], [[RESP]] : !cir.double, !cir.ptr +// LLVM-NEXT: store double [[PHIV]], ptr [[RESP]], align 8, +// AFTER: cir.va.end [[VARLIST]] : !cir.ptr +// LLVM: call void @llvm.va_end.p0(ptr [[VARLIST]]), +// AFTER: [[RES:%.*]] = cir.load [[RESP]] : !cir.ptr, !cir.double +// LLVM: [[RES:%.*]] = load double, ptr [[RESP]], align 8, +// AFTER: cir.store [[RES]], [[RETP]] : !cir.double, !cir.ptr +// LLVM: store double [[RES]], ptr [[RETP]], align 8, +// AFTER: [[RETV:%.*]] = cir.load [[RETP]] : !cir.ptr, !cir.double +// LLVM: [[RETV:%.*]] = load double, ptr [[RETP]], align 8, +// AFTER: cir.return [[RETV]] : !cir.double +// LLVM-NEXT: ret double [[RETV]], + +// AFTER: [[BB_MAY_REG]]: // pred: [[BB_BEGIN:\^bb.*]] +// LLVM: [[BB_MAY_REG]]: ; preds = %[[BB_BEGIN:.*]] +// AFTER-NEXT: [[SIXTEEN:%.*]] = cir.const #cir.int<16> : !s32i +// AFTER-NEXT: [[NEW_REG_OFFS:%.*]] = cir.binop(add, [[VR_OFFS]], [[SIXTEEN]]) : !s32i +// LLVM-NEXT: [[NEW_REG_OFFS:%.*]] = add i32 [[VR_OFFS]], 16, +// AFTER-NEXT: cir.store [[NEW_REG_OFFS]], [[VR_OFFS_P]] : !s32i, !cir.ptr +// LLVM-NEXT: store i32 [[NEW_REG_OFFS]], ptr [[VR_OFFS_P]], align 4, +// AFTER-NEXT: [[CMP1:%.*]] = cir.cmp(le, [[NEW_REG_OFFS]], [[ZERO]]) : !s32i, !cir.bool +// LLVM-NEXT: [[CMP1:%.*]] = icmp sle i32 [[NEW_REG_OFFS]], 0, +// AFTER-NEXT: cir.brcond [[CMP1]] [[BB_IN_REG]], [[BB_ON_STACK]] +// LLVM-NEXT: br i1 [[CMP1]], label %[[BB_IN_REG]], label %[[BB_ON_STACK]], + +// AFTER: [[BB_IN_REG]]: // pred: [[BB_MAY_REG]] +// LLVM: [[BB_IN_REG]]: ; preds = %[[BB_MAY_REG]] +// AFTER-NEXT: [[VR_TOP_P:%.*]] = cir.get_member [[VARLIST]][2] {name = "vr_top"} : !cir.ptr -> !cir.ptr> +// LLVM-NEXT: [[VR_TOP_P:%.*]] = getelementptr %struct.__va_list, ptr [[VARLIST]], i32 0, i32 2, +// AFTER-NEXT: [[VR_TOP:%.*]] = cir.load [[VR_TOP_P]] : !cir.ptr>, !cir.ptr +// LLVM-NEXT: [[VR_TOP:%.*]] = load ptr, ptr [[VR_TOP_P]], align 8, +// AFTER-NEXT: [[TMP2:%.*]] = cir.cast(bitcast, [[VR_TOP]] : !cir.ptr), !cir.ptr +// AFTER-NEXT: [[TMP3:%.*]] = cir.ptr_stride([[TMP2]] : !cir.ptr, [[VR_OFFS]] : !s32i), !cir.ptr +// LLVM-NEXT: [[EXT64_VR_OFFS:%.*]] = sext i32 [[VR_OFFS]] to i64, +// LLVM-NEXT: [[IN_REG_OUTPUT]] = getelementptr i8, ptr [[VR_TOP]], i64 [[EXT64_VR_OFFS]], +// AFTER-NEXT: [[IN_REG_OUTPUT:%.*]] = cir.cast(bitcast, [[TMP3]] : !cir.ptr), !cir.ptr +// AFTER-NEXT: cir.br [[BB_END]]([[IN_REG_OUTPUT]] : !cir.ptr) +// LLVM-NEXT: br label %[[BB_END]], + +// AFTER: [[BB_ON_STACK]]: // 2 preds: [[BB_BEGIN]], [[BB_MAY_REG]] +// LLVM: [[BB_ON_STACK]]: ; preds = %[[BB_MAY_REG]], %[[BB_BEGIN]] +// AFTER-NEXT: [[STACK_P:%.*]] = cir.get_member [[VARLIST]][0] {name = "stack"} : !cir.ptr -> !cir.ptr> +// LLVM-NEXT: [[STACK_P:%.*]] = getelementptr %struct.__va_list, ptr [[VARLIST]], i32 0, i32 0, +// AFTER-NEXT: [[STACK_V:%.*]] = cir.load [[STACK_P]] : !cir.ptr>, !cir.ptr +// LLVM-NEXT: [[STACK_V]] = load ptr, ptr [[STACK_P]], align 8, +// AFTER-NEXT: [[EIGHT_IN_PTR_ARITH:%.*]] = cir.const #cir.int<8> : !u64i +// AFTER-NEXT: [[TMP4:%.*]] = cir.cast(bitcast, [[STACK_V]] : !cir.ptr), !cir.ptr +// AFTER-NEXT: [[TMP5:%.*]] = cir.ptr_stride([[TMP4]] : !cir.ptr, [[EIGHT_IN_PTR_ARITH]] : !u64i), !cir.ptr +// LLVM-NEXT: [[NEW_STACK_V:%.*]] = getelementptr i8, ptr [[STACK_V]], i32 8, +// AFTER-NEXT: [[NEW_STACK_V:%.*]] = cir.cast(bitcast, [[TMP5]] : !cir.ptr), !cir.ptr +// AFTER-NEXT: cir.store [[NEW_STACK_V]], [[STACK_P]] : !cir.ptr, !cir.ptr> +// LLVM-NEXT: store ptr [[NEW_STACK_V]], ptr [[STACK_P]], align 8, +// AFTER-NEXT: cir.br [[BB_END]]([[STACK_V]] : !cir.ptr) +// LLVM-NEXT: br label %[[BB_END]], diff --git a/clang/test/CIR/CodeGen/var-arg.c b/clang/test/CIR/CodeGen/var-arg.c new file mode 100644 index 000000000000..5dc330a17fd7 --- /dev/null +++ b/clang/test/CIR/CodeGen/var-arg.c @@ -0,0 +1,108 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=BEFORE +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=AFTER +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM +#include + +int f1(int n, ...) { + va_list valist; + va_start(valist, n); + int res = va_arg(valist, int); + va_end(valist); + return res; +} + +// BEFORE: !ty_22__va_list22 = !cir.struct, !cir.ptr, !cir.ptr, !cir.int, !cir.int} +// BEFORE: cir.func @f1(%arg0: !s32i, ...) -> !s32i +// BEFORE: [[RETP:%.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// BEFORE: [[RESP:%.*]] = cir.alloca !s32i, !cir.ptr, ["res", init] +// BEFORE: cir.va.start [[VARLIST:%.*]] : !cir.ptr +// BEFORE: [[TMP0:%.*]] = cir.va.arg [[VARLIST]] : (!cir.ptr) -> !s32i +// BEFORE: cir.store [[TMP0]], [[RESP]] : !s32i, !cir.ptr +// BEFORE: cir.va.end [[VARLIST]] : !cir.ptr +// BEFORE: [[RES:%.*]] = cir.load [[RESP]] : !cir.ptr, !s32i +// BEFORE: cir.store [[RES]], [[RETP]] : !s32i, !cir.ptr +// BEFORE: [[RETV:%.*]] = cir.load [[RETP]] : !cir.ptr, !s32i +// BEFORE: cir.return [[RETV]] : !s32i + +// AFTER: !ty_22__va_list22 = !cir.struct, !cir.ptr, !cir.ptr, !cir.int, !cir.int} +// LLVM: %struct.__va_list = type { ptr, ptr, ptr, i32, i32 } +// AFTER: cir.func @f1(%arg0: !s32i, ...) -> !s32i +// LLVM: define i32 @f1(i32 %0, ...) +// LLVM: [[ARGN:%.*]] = alloca i32, i64 1, align 4, +// AFTER: [[RETP:%.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// LLVM: [[RETP:%.*]] = alloca i32, i64 1, align 4, +// AFTER: [[RESP:%.*]] = cir.alloca !s32i, !cir.ptr, ["res", init] +// LLVM: [[RESP:%.*]] = alloca i32, i64 1, align 4, +// AFTER: cir.va.start [[VARLIST:%.*]] : !cir.ptr +// LLVM: call void @llvm.va_start.p0(ptr [[VARLIST:%.*]]), +// AFTER: [[GR_OFFS_P:%.*]] = cir.get_member [[VARLIST]][3] {name = "gr_offs"} : !cir.ptr -> !cir.ptr +// LLVM: [[GR_OFFS_P:%.*]] = getelementptr %struct.__va_list, ptr [[VARLIST]], i32 0, i32 3 +// AFTER: [[GR_OFFS:%.*]] = cir.load [[GR_OFFS_P]] : !cir.ptr, !s32i +// LLVM: [[GR_OFFS:%.*]] = load i32, ptr [[GR_OFFS_P]], align 4, +// AFTER: [[ZERO:%.*]] = cir.const #cir.int<0> : !s32i +// AFTER: [[CMP0:%.*]] = cir.cmp(ge, [[GR_OFFS]], [[ZERO]]) : !s32i, !cir.bool +// LLVM-NEXT: [[CMP0:%.*]] = icmp sge i32 [[GR_OFFS]], 0, +// AFTER-NEXT: cir.brcond [[CMP0]] [[BB_ON_STACK:\^bb.*]], [[BB_MAY_REG:\^bb.*]] +// LLVM-NEXT: br i1 [[CMP0]], label %[[BB_ON_STACK:.*]], label %[[BB_MAY_REG:.*]], + +// AFTER-NEXT: [[BB_END:\^bb.*]]([[BLK_ARG:%.*]]: !cir.ptr): // 2 preds: [[BB_IN_REG:\^bb.*]], [[BB_ON_STACK]] +// LLVM: [[BB_END:.*]]: ; preds = %[[BB_ON_STACK]], %[[BB_IN_REG:.*]] +// AFTER-NEXT: [[TMP0:%.*]] = cir.cast(bitcast, [[BLK_ARG]] : !cir.ptr), !cir.ptr +// LLVM-NEXT: [[PHIP:%.*]] = phi ptr [ [[IN_REG_OUTPUT:%.*]], %[[BB_IN_REG]] ], [ [[STACK_V:%.*]], %[[BB_ON_STACK]] ] +// AFTER-NEXT: [[TMP1:%.*]] = cir.load [[TMP0]] : !cir.ptr, !s32i +// LLVM-NEXT: [[PHIV:%.*]] = load i32, ptr [[PHIP]], align 4, +// AFTER: cir.store [[TMP1]], [[RESP]] : !s32i, !cir.ptr +// LLVM-NEXT: store i32 [[PHIV]], ptr [[RESP]], align 4, +// AFTER: cir.va.end [[VARLIST]] : !cir.ptr +// LLVM: call void @llvm.va_end.p0(ptr [[VARLIST]]), +// AFTER: [[RES:%.*]] = cir.load [[RESP]] : !cir.ptr, !s32i +// LLVM: [[RES:%.*]] = load i32, ptr [[RESP]], align 4, +// AFTER: cir.store [[RES]], [[RETP]] : !s32i, !cir.ptr +// LLVM: store i32 [[RES]], ptr [[RETP]], align 4, +// AFTER: [[RETV:%.*]] = cir.load [[RETP]] : !cir.ptr, !s32i +// LLVM: [[RETV:%.*]] = load i32, ptr [[RETP]], align 4, +// AFTER: cir.return [[RETV]] : !s32i +// LLVM-NEXT: ret i32 [[RETV]], + +// AFTER: [[BB_MAY_REG]]: // pred: [[BB_BEGIN:\^bb.*]] +// LLVM: [[BB_MAY_REG]]: ; preds = %[[BB_BEGIN:.*]] +// AFTER-NEXT: [[EIGHT:%.*]] = cir.const #cir.int<8> : !s32i +// AFTER-NEXT: [[NEW_REG_OFFS:%.*]] = cir.binop(add, [[GR_OFFS]], [[EIGHT]]) : !s32i +// LLVM: [[NEW_REG_OFFS:%.*]] = add i32 [[GR_OFFS]], 8, +// AFTER-NEXT: cir.store [[NEW_REG_OFFS]], [[GR_OFFS_P]] : !s32i, !cir.ptr +// LLVM: store i32 [[NEW_REG_OFFS]], ptr [[GR_OFFS_P]], align 4, +// AFTER-NEXT: [[CMP1:%.*]] = cir.cmp(le, [[NEW_REG_OFFS]], [[ZERO]]) : !s32i, !cir.bool +// LLVM-NEXT: [[CMP1:%.*]] = icmp sle i32 [[NEW_REG_OFFS]], 0, +// AFTER-NEXT: cir.brcond [[CMP1]] [[BB_IN_REG]], [[BB_ON_STACK]] +// LLVM-NEXT: br i1 [[CMP1]], label %[[BB_IN_REG]], label %[[BB_ON_STACK]], + +// AFTER: [[BB_IN_REG]]: // pred: [[BB_MAY_REG]] +// LLVM: [[BB_IN_REG]]: ; preds = %[[BB_MAY_REG]] +// AFTER-NEXT: [[GR_TOP_P:%.*]] = cir.get_member [[VARLIST]][1] {name = "gr_top"} : !cir.ptr -> !cir.ptr> +// LLVM-NEXT: [[GR_TOP_P:%.*]] = getelementptr %struct.__va_list, ptr [[VARLIST]], i32 0, i32 1, +// AFTER-NEXT: [[GR_TOP:%.*]] = cir.load [[GR_TOP_P]] : !cir.ptr>, !cir.ptr +// LLVM-NEXT: [[GR_TOP:%.*]] = load ptr, ptr [[GR_TOP_P]], align 8, +// AFTER-NEXT: [[TMP2:%.*]] = cir.cast(bitcast, [[GR_TOP]] : !cir.ptr), !cir.ptr +// AFTER-NEXT: [[TMP3:%.*]] = cir.ptr_stride([[TMP2]] : !cir.ptr, [[GR_OFFS]] : !s32i), !cir.ptr +// LLVM-NEXT: [[EXT64_GR_OFFS:%.*]] = sext i32 [[GR_OFFS]] to i64, +// LLVM-NEXT: [[IN_REG_OUTPUT]] = getelementptr i8, ptr [[GR_TOP]], i64 [[EXT64_GR_OFFS]], +// AFTER-NEXT: [[IN_REG_OUTPUT:%.*]] = cir.cast(bitcast, [[TMP3]] : !cir.ptr), !cir.ptr +// AFTER-NEXT: cir.br [[BB_END]]([[IN_REG_OUTPUT]] : !cir.ptr) +// LLVM-NEXT: br label %[[BB_END]], + +// AFTER: [[BB_ON_STACK]]: // 2 preds: [[BB_BEGIN]], [[BB_MAY_REG]] +// LLVM: [[BB_ON_STACK]]: ; preds = %[[BB_MAY_REG]], %[[BB_BEGIN]] +// AFTER-NEXT: [[STACK_P:%.*]] = cir.get_member [[VARLIST]][0] {name = "stack"} : !cir.ptr -> !cir.ptr> +// LLVM-NEXT: [[STACK_P:%.*]] = getelementptr %struct.__va_list, ptr [[VARLIST]], i32 0, i32 0, +// AFTER-NEXT: [[STACK_V:%.*]] = cir.load [[STACK_P]] : !cir.ptr>, !cir.ptr +// LLVM-NEXT: [[STACK_V]] = load ptr, ptr [[STACK_P]], align 8, +// AFTER-NEXT: [[EIGHT_IN_PTR_ARITH:%.*]] = cir.const #cir.int<8> : !u64i +// AFTER-NEXT: [[TMP4:%.*]] = cir.cast(bitcast, [[STACK_V]] : !cir.ptr), !cir.ptr +// AFTER-NEXT: [[TMP5:%.*]] = cir.ptr_stride([[TMP4]] : !cir.ptr, [[EIGHT_IN_PTR_ARITH]] : !u64i), !cir.ptr +// LLVM-NEXT: [[NEW_STACK_V:%.*]] = getelementptr i8, ptr [[STACK_V]], i32 8, +// AFTER-NEXT: [[NEW_STACK_V:%.*]] = cir.cast(bitcast, [[TMP5]] : !cir.ptr), !cir.ptr +// AFTER-NEXT: cir.store [[NEW_STACK_V]], [[STACK_P]] : !cir.ptr, !cir.ptr> +// LLVM-NEXT: store ptr [[NEW_STACK_V]], ptr [[STACK_P]], align 8, +// AFTER-NEXT: cir.br [[BB_END]]([[STACK_V]] : !cir.ptr) +// LLVM-NEXT: br label %[[BB_END]], diff --git a/clang/test/CIR/CodeGen/variadics.c b/clang/test/CIR/CodeGen/variadics.c index 90ab27cc8ae5..57f3885c8a35 100644 --- a/clang/test/CIR/CodeGen/variadics.c +++ b/clang/test/CIR/CodeGen/variadics.c @@ -3,7 +3,7 @@ // RUN: %clang_cc1 -x c++ -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s // RUN: %clang_cc1 -x c++ -std=c++20 -triple aarch64-none-linux-android24 -fclangir -emit-cir %s -o %t.cir -// RUN: FileCheck --input-file=%t.cir %s +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=ARM64_CHECK typedef __builtin_va_list va_list; @@ -13,24 +13,29 @@ typedef __builtin_va_list va_list; #define va_copy(dst, src) __builtin_va_copy(dst, src) // CHECK: [[VALISTTYPE:!.+va_list.*]] = !cir.struct !s32i +// AMR64_CHECK: cir.func @{{.*}}average{{.*}}(%arg0: !s32i loc({{.+}}), ...) -> !s32i va_list args, args_copy; va_start(args, count); // CHECK: cir.va.start %{{[0-9]+}} : !cir.ptr<[[VALISTTYPE]]> + // ARM64_CHECK: cir.va.start %{{[0-9]+}} : !cir.ptr<[[VALISTTYPE]]> va_copy(args_copy, args); // CHECK: cir.va.copy %{{[0-9]+}} to %{{[0-9]+}} : !cir.ptr<[[VALISTTYPE]]>, !cir.ptr<[[VALISTTYPE]]> - + // ARM64_CHECK: cir.va.copy %{{[0-9]+}} to %{{[0-9]+}} : !cir.ptr<[[VALISTTYPE]]>, !cir.ptr<[[VALISTTYPE]]> int sum = 0; for(int i = 0; i < count; i++) { sum += va_arg(args, int); // CHECK: %{{[0-9]+}} = cir.va.arg %{{[0-9]+}} : (!cir.ptr<[[VALISTTYPE]]>) -> !s32i + // ARM64_CHECK-NOT: %{{[0-9]+}} = cir.va.arg %{{[0-9]+}} : (!cir.ptr<[[VALISTTYPE]]>) -> !s32i } va_end(args); // CHECK: cir.va.end %{{[0-9]+}} : !cir.ptr<[[VALISTTYPE]]> + // ARM64_CHECK: cir.va.end %{{[0-9]+}} : !cir.ptr<[[VALISTTYPE]]> return count > 0 ? sum / count : 0; } @@ -38,4 +43,5 @@ int average(int count, ...) { int test(void) { return average(5, 1, 2, 3, 4, 5); // CHECK: cir.call @{{.*}}average{{.*}}(%{{[0-9]+}}, %{{[0-9]+}}, %{{[0-9]+}}, %{{[0-9]+}}, %{{[0-9]+}}, %{{[0-9]+}}) : (!s32i, !s32i, !s32i, !s32i, !s32i, !s32i) -> !s32i + // ARM64_CHECK: cir.call @{{.*}}average{{.*}}(%{{[0-9]+}}, %{{[0-9]+}}, %{{[0-9]+}}, %{{[0-9]+}}, %{{[0-9]+}}, %{{[0-9]+}}) : (!s32i, !s32i, !s32i, !s32i, !s32i, !s32i) -> !s32i }