Skip to content

Commit

Permalink
round 4: 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 10, 2024
1 parent 3bad644 commit 3adfff7
Show file tree
Hide file tree
Showing 12 changed files with 528 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
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ class TargetCIRGenInfo {
virtual ~TargetCIRGenInfo() {}
};

enum class AArch64ABIKind {
AAPCS = 0,
DarwinPCS,
Win64,
AAPCSSoft,
};

} // namespace cir

#endif
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
34 changes: 28 additions & 6 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//

#include "../../CodeGen/TargetInfo.h"
#include "LoweringPrepareCXXABI.h"
#include "PassDetail.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
Expand All @@ -15,6 +16,7 @@
#include "clang/AST/CharUnits.h"
#include "clang/AST/Mangle.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/Passes.h"
Expand All @@ -28,6 +30,7 @@

#include <memory>

using cir::AArch64ABIKind;
using cir::CIRBaseBuilderTy;
using namespace mlir;
using namespace mlir::cir;
Expand Down Expand Up @@ -70,6 +73,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,15 +112,18 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {

void setASTContext(clang::ASTContext *c) {
astCtx = c;
auto abiStr = c->getTargetInfo().getABI();
switch (c->getCXXABIKind()) {
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());
assert(abiStr == "aapcs" || abiStr == "darwinpcs");
cxxABI.reset(::cir::LoweringPrepareCXXABI::createAArch64ABI(
abiStr == "aapcs" ? AArch64ABIKind::AAPCS
: AArch64ABIKind::DarwinPCS));
break;

default:
llvm_unreachable("NYI");
}
Expand Down Expand Up @@ -320,6 +327,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 +622,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 +656,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
183 changes: 183 additions & 0 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepareAArch64CXXABI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
//====- 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 "../../CodeGen/TargetInfo.h"
#include "LoweringPrepareItaniumCXXABI.h"

#include <assert.h>

using cir::LoweringPrepareCXXABI;
using cir::MissingFeatures;
using cir::AArch64ABIKind;

using namespace mlir;
using namespace mlir::cir;

namespace {
class LoweringPrepareAArch64CXXABI : public LoweringPrepareItaniumCXXABI {
public:
LoweringPrepareAArch64CXXABI(AArch64ABIKind k) : Kind(k) {}
mlir::Value lowerVAArg(CIRBaseBuilderTy &builder,
mlir::cir::VAArgOp op) override;

private:
AArch64ABIKind Kind;
mlir::Value lowerAAPCSVAArg(CIRBaseBuilderTy &builder, mlir::cir::VAArgOp op);
};
} // namespace

LoweringPrepareCXXABI *
LoweringPrepareCXXABI::createAArch64ABI(AArch64ABIKind 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 == AArch64ABIKind::AAPCS) {
return lowerAAPCSVAArg(builder, op);
}
assert(0 && "unsupported AArch64 CXX ABI");
}
4 changes: 4 additions & 0 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#ifndef LLVM_CLANG_LIB_CIR_LOWERING_PREPARE_CXX_ABI_H
#define LLVM_CLANG_LIB_CIR_LOWERING_PREPARE_CXX_ABI_H

#include "../../CodeGen/TargetInfo.h"
#include "mlir/IR/Value.h"
#include "clang/AST/ASTContext.h"
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
Expand All @@ -25,7 +26,10 @@ namespace cir {
class LoweringPrepareCXXABI {
public:
static LoweringPrepareCXXABI *createItaniumABI();
static LoweringPrepareCXXABI *createAArch64ABI(AArch64ABIKind k);

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

virtual mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder,
Expand Down
Loading

0 comments on commit 3adfff7

Please sign in to comment.