Skip to content

Commit

Permalink
AArch64 ABI lowering cir.var_arg, squashed related commits
Browse files Browse the repository at this point in the history
  • Loading branch information
ghehg committed May 9, 2024
1 parent 3e4d5e6 commit 2e48c55
Show file tree
Hide file tree
Showing 11 changed files with 513 additions and 27 deletions.
13 changes: 13 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,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<mlir::cir::PointerType>());
auto structBaseTy =
structPtr.getType().cast<mlir::cir::PointerType>().getPointee();
assert(structBaseTy.isa<mlir::cir::StructType>());
auto fldTy = structBaseTy.cast<mlir::cir::StructType>().getMembers()[idx];
auto fldPtrTy = ::mlir::cir::PointerType::get(getContext(), fldTy);
return create<mlir::cir::GetMemberOp>(loc, fldPtrTy, structPtr, fldName,
idx);
}

mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) {
return createCast(mlir::cir::CastKind::ptr_to_int, src, newTy);
}
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/Dialect/IR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ struct MissingFeatures {
// C++ ABI support
static bool cxxABI() { return false; }
static bool setCallingConv() { return false; }
static bool handleBigEndian() { return false; }
static bool handleAArch64Indirect() { return false; }

// Address space related
static bool addressSpace() { return false; }
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ add_clang_library(MLIRCIRTransforms
LifetimeCheck.cpp
LoweringPrepare.cpp
LoweringPrepareItaniumCXXABI.cpp
LoweringPrepareAArch64CXXABI.cpp
MergeCleanups.cpp
DropAST.cpp
IdiomRecognizer.cpp
Expand Down
29 changes: 23 additions & 6 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {

void runOnOp(Operation *op);
void lowerThreeWayCmpOp(CmpThreeWayOp op);
void lowerVAArgOp(VAArgOp op);
void lowerGlobalOp(GlobalOp op);
void lowerDynamicCastOp(DynamicCastOp op);
void lowerStdFindOp(StdFindOp op);
Expand Down Expand Up @@ -108,13 +109,14 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {

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:
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -603,6 +617,8 @@ void LoweringPreparePass::lowerIterEndOp(IterEndOp op) {
void LoweringPreparePass::runOnOp(Operation *op) {
if (auto threeWayCmp = dyn_cast<CmpThreeWayOp>(op)) {
lowerThreeWayCmpOp(threeWayCmp);
} else if (auto vaArgOp = dyn_cast<VAArgOp>(op)) {
lowerVAArgOp(vaArgOp);
} else if (auto getGlobal = dyn_cast<GlobalOp>(op)) {
lowerGlobalOp(getGlobal);
} else if (auto dynamicCast = dyn_cast<DynamicCastOp>(op)) {
Expand Down Expand Up @@ -635,8 +651,9 @@ void LoweringPreparePass::runOnOperation() {

SmallVector<Operation *> opsToTransform;
op->walk([&](Operation *op) {
if (isa<CmpThreeWayOp, GlobalOp, DynamicCastOp, StdFindOp, IterEndOp,
IterBeginOp, ArrayCtor, ArrayDtor, mlir::cir::FuncOp>(op))
if (isa<CmpThreeWayOp, VAArgOp, GlobalOp, DynamicCastOp, StdFindOp,
IterEndOp, IterBeginOp, ArrayCtor, ArrayDtor, mlir::cir::FuncOp>(
op))
opsToTransform.push_back(op);
});

Expand Down
181 changes: 181 additions & 0 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepareAArch64CXXABI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
//====- 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 <assert.h>

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 lowerAAPCSVAArg(CIRBaseBuilderTy &builder, mlir::cir::VAArgOp op);
};
} // namespace

LoweringPrepareCXXABI *
LoweringPrepareCXXABI::createAArch64ABI(clang::TargetCXXABI::Kind k) {
return new LoweringPrepareAArch64CXXABI(k);
}

mlir::Value
LoweringPrepareAArch64CXXABI::lowerAAPCSVAArg(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<mlir::cir::IntType, mlir::cir::SingleType,
mlir::cir::PointerType, mlir::cir::BoolType,
mlir::cir::DoubleType>();

// Homogenous Aggregate type not supported and indirect arg
// passing not supported yet. And for these supported types,
// we should not have alignment greater than 8 problem.
assert(isSupportedType);
assert(!MissingFeatures::handleAArch64Indirect());

bool isFloatingType =
opResTy.isa<mlir::cir::SingleType, mlir::cir::DoubleType>();

// 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<mlir::cir::LoadOp>(loc, offsP);
auto zeroValue = builder.create<mlir::cir::ConstantOp>(
loc, offs.getType(), mlir::cir::IntAttr::get(offs.getType(), 0));
auto cmpRes = builder.create<mlir::cir::CmpOp>(loc, boolTy, CmpOpKind::ge,
offs, zeroValue);
builder.create<mlir::cir::BrCondOp>(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<mlir::cir::ConstantOp>(
loc, offs.getType(),
mlir::cir::IntAttr::get(offs.getType(), isFloatingType ? 16 : 8));
auto newRegsOffs = builder.create<mlir::cir::BinOp>(
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<mlir::cir::CmpOp>(
loc, boolTy, CmpOpKind::le, newRegsOffs, zeroValue);
builder.create<mlir::cir::BrCondOp>(loc, maybeRegCmpRes, inRegBlock,
onStackBlock);

//=======================================
// Argument was on the stack
//=======================================
builder.setInsertionPointToEnd(onStackBlock);
auto stackP = builder.createGetMemberOp(loc, valist, "stack", 0);
// On big-endian platforms, the value will be right-aligned in its stack slot.
assert(!MissingFeatures::handleBigEndian());
auto stack = builder.create<mlir::cir::LoadOp>(loc, stackP);
auto ptrDiffTy =
mlir::cir::IntType::get(builder.getContext(), 64, /*signed=*/false);
auto eight = builder.create<mlir::cir::ConstantOp>(
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<mlir::cir::PtrStrideOp>(
loc, castStack.getType(), castStack, eight);
auto newStack = builder.createBitcast(newStackAsi8Ptr, stack.getType());
builder.createStore(loc, newStack, stackP);
builder.create<mlir::cir::BrOp>(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<mlir::cir::LoadOp>(loc, regTopP);
auto castRegTop = builder.createBitcast(regTop, i8PtrTy);
// On big-endian platforms, the value will be right-aligned in its stack slot.
assert(!MissingFeatures::handleBigEndian());
auto resAsInt8P = builder.create<mlir::cir::PtrStrideOp>(
loc, castRegTop.getType(), castRegTop, offs);
auto resAsVoidP = builder.createBitcast(resAsInt8P, regTop.getType());
builder.create<mlir::cir::BrOp>(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<mlir::cir::PointerType>());
auto opResPTy = PointerType::get(builder.getContext(), opResTy);
auto castResP = builder.createBitcast(resP, opResPTy);
auto res = builder.create<mlir::cir::LoadOp>(loc, castResP);
return res.getResult();
}

mlir::Value LoweringPrepareAArch64CXXABI::lowerVAArg(CIRBaseBuilderTy &builder,
mlir::cir::VAArgOp op) {

if (Kind == clang::TargetCXXABI::GenericAArch64) {
return lowerAAPCSVAArg(builder, op);
}
assert(0 && "unsupported AArch64 CXX ABI");
}
3 changes: 3 additions & 0 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ namespace cir {
class LoweringPrepareCXXABI {
public:
static LoweringPrepareCXXABI *createItaniumABI();
static LoweringPrepareCXXABI *createAArch64ABI(clang::TargetCXXABI::Kind k);

virtual mlir::Value lowerVAArg(CIRBaseBuilderTy &builder,
mlir::cir::VAArgOp op) = 0;
virtual ~LoweringPrepareCXXABI() {}

virtual mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder,
Expand Down
29 changes: 15 additions & 14 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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"
Expand All @@ -21,18 +22,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,
clang::ASTContext &astCtx,
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();
Expand Down Expand Up @@ -169,3 +163,10 @@ LoweringPrepareItaniumCXXABI::lowerDynamicCast(CIRBaseBuilderTy &builder,
})
.getResult();
}

mlir::Value LoweringPrepareItaniumCXXABI::lowerVAArg(CIRBaseBuilderTy &builder,
mlir::cir::VAArgOp op) {
// There is no generic cir lowering for var_arg, here we just return empty
// so preserve cir.var_arg and leave responsibility of lowering to LowerToLLVM
llvm_unreachable("NYI");
}
25 changes: 25 additions & 0 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//====- 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,
clang::ASTContext &astCtx,
mlir::cir::DynamicCastOp op) override;
mlir::Value lowerVAArg(CIRBaseBuilderTy &builder,
mlir::cir::VAArgOp op) override;
};
Loading

0 comments on commit 2e48c55

Please sign in to comment.