Skip to content

Commit 3adfff7

Browse files
committed
round 4: AArch64 ABI lowering cir.var_arg, squashed related commits
1 parent 3bad644 commit 3adfff7

12 files changed

+528
-27
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

+13
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,19 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
264264
return createCast(mlir::cir::CastKind::int_to_ptr, src, newTy);
265265
}
266266

267+
mlir::Value createGetMemberOp(mlir::Location &loc, mlir::Value structPtr,
268+
const char *fldName, unsigned idx) {
269+
270+
assert(structPtr.getType().isa<mlir::cir::PointerType>());
271+
auto structBaseTy =
272+
structPtr.getType().cast<mlir::cir::PointerType>().getPointee();
273+
assert(structBaseTy.isa<mlir::cir::StructType>());
274+
auto fldTy = structBaseTy.cast<mlir::cir::StructType>().getMembers()[idx];
275+
auto fldPtrTy = ::mlir::cir::PointerType::get(getContext(), fldTy);
276+
return create<mlir::cir::GetMemberOp>(loc, fldPtrTy, structPtr, fldName,
277+
idx);
278+
}
279+
267280
mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) {
268281
return createCast(mlir::cir::CastKind::ptr_to_int, src, newTy);
269282
}

clang/lib/CIR/CodeGen/TargetInfo.h

+7
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ class TargetCIRGenInfo {
6868
virtual ~TargetCIRGenInfo() {}
6969
};
7070

71+
enum class AArch64ABIKind {
72+
AAPCS = 0,
73+
DarwinPCS,
74+
Win64,
75+
AAPCSSoft,
76+
};
77+
7178
} // namespace cir
7279

7380
#endif

clang/lib/CIR/Dialect/IR/MissingFeatures.h

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ struct MissingFeatures {
2121
// C++ ABI support
2222
static bool cxxABI() { return false; }
2323
static bool setCallingConv() { return false; }
24+
static bool handleBigEndian() { return false; }
25+
static bool handleAArch64Indirect() { return false; }
2426

2527
// Address space related
2628
static bool addressSpace() { return false; }

clang/lib/CIR/Dialect/Transforms/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ add_clang_library(MLIRCIRTransforms
22
LifetimeCheck.cpp
33
LoweringPrepare.cpp
44
LoweringPrepareItaniumCXXABI.cpp
5+
LoweringPrepareAArch64CXXABI.cpp
56
MergeCleanups.cpp
67
DropAST.cpp
78
IdiomRecognizer.cpp

clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp

+28-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "../../CodeGen/TargetInfo.h"
910
#include "LoweringPrepareCXXABI.h"
1011
#include "PassDetail.h"
1112
#include "mlir/Dialect/Func/IR/FuncOps.h"
@@ -15,6 +16,7 @@
1516
#include "clang/AST/CharUnits.h"
1617
#include "clang/AST/Mangle.h"
1718
#include "clang/Basic/Module.h"
19+
#include "clang/Basic/TargetInfo.h"
1820
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
1921
#include "clang/CIR/Dialect/IR/CIRDialect.h"
2022
#include "clang/CIR/Dialect/Passes.h"
@@ -28,6 +30,7 @@
2830

2931
#include <memory>
3032

33+
using cir::AArch64ABIKind;
3134
using cir::CIRBaseBuilderTy;
3235
using namespace mlir;
3336
using namespace mlir::cir;
@@ -70,6 +73,7 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
7073

7174
void runOnOp(Operation *op);
7275
void lowerThreeWayCmpOp(CmpThreeWayOp op);
76+
void lowerVAArgOp(VAArgOp op);
7377
void lowerGlobalOp(GlobalOp op);
7478
void lowerDynamicCastOp(DynamicCastOp op);
7579
void lowerStdFindOp(StdFindOp op);
@@ -108,15 +112,18 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
108112

109113
void setASTContext(clang::ASTContext *c) {
110114
astCtx = c;
115+
auto abiStr = c->getTargetInfo().getABI();
111116
switch (c->getCXXABIKind()) {
112117
case clang::TargetCXXABI::GenericItanium:
118+
cxxABI.reset(::cir::LoweringPrepareCXXABI::createItaniumABI());
119+
break;
113120
case clang::TargetCXXABI::GenericAArch64:
114121
case clang::TargetCXXABI::AppleARM64:
115-
// TODO: this isn't quite right, clang uses AppleARM64CXXABI which
116-
// inherits from ARMCXXABI. We'll have to follow suit.
117-
cxxABI.reset(::cir::LoweringPrepareCXXABI::createItaniumABI());
122+
assert(abiStr == "aapcs" || abiStr == "darwinpcs");
123+
cxxABI.reset(::cir::LoweringPrepareCXXABI::createAArch64ABI(
124+
abiStr == "aapcs" ? AArch64ABIKind::AAPCS
125+
: AArch64ABIKind::DarwinPCS));
118126
break;
119-
120127
default:
121128
llvm_unreachable("NYI");
122129
}
@@ -320,6 +327,18 @@ static void canonicalizeIntrinsicThreeWayCmp(CIRBaseBuilderTy &builder,
320327
op.erase();
321328
}
322329

330+
void LoweringPreparePass::lowerVAArgOp(VAArgOp op) {
331+
CIRBaseBuilderTy builder(getContext());
332+
builder.setInsertionPoint(op);
333+
334+
auto res = cxxABI->lowerVAArg(builder, op);
335+
if (res) {
336+
op.replaceAllUsesWith(res);
337+
op.erase();
338+
}
339+
return;
340+
}
341+
323342
void LoweringPreparePass::lowerThreeWayCmpOp(CmpThreeWayOp op) {
324343
CIRBaseBuilderTy builder(getContext());
325344
builder.setInsertionPointAfter(op);
@@ -603,6 +622,8 @@ void LoweringPreparePass::lowerIterEndOp(IterEndOp op) {
603622
void LoweringPreparePass::runOnOp(Operation *op) {
604623
if (auto threeWayCmp = dyn_cast<CmpThreeWayOp>(op)) {
605624
lowerThreeWayCmpOp(threeWayCmp);
625+
} else if (auto vaArgOp = dyn_cast<VAArgOp>(op)) {
626+
lowerVAArgOp(vaArgOp);
606627
} else if (auto getGlobal = dyn_cast<GlobalOp>(op)) {
607628
lowerGlobalOp(getGlobal);
608629
} else if (auto dynamicCast = dyn_cast<DynamicCastOp>(op)) {
@@ -635,8 +656,9 @@ void LoweringPreparePass::runOnOperation() {
635656

636657
SmallVector<Operation *> opsToTransform;
637658
op->walk([&](Operation *op) {
638-
if (isa<CmpThreeWayOp, GlobalOp, DynamicCastOp, StdFindOp, IterEndOp,
639-
IterBeginOp, ArrayCtor, ArrayDtor, mlir::cir::FuncOp>(op))
659+
if (isa<CmpThreeWayOp, VAArgOp, GlobalOp, DynamicCastOp, StdFindOp,
660+
IterEndOp, IterBeginOp, ArrayCtor, ArrayDtor, mlir::cir::FuncOp>(
661+
op))
640662
opsToTransform.push_back(op);
641663
});
642664

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
//====- LoweringPrepareArm64CXXABI.cpp - Arm64 ABI specific code -----====//
2+
//
3+
// Part of the LLVM Project,
4+
// under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===------------------------------------------------------------------===//
9+
//
10+
// This file provides ARM64 C++ ABI specific code that is used during LLVMIR
11+
// lowering prepare.
12+
//
13+
//===------------------------------------------------------------------===//
14+
15+
#include "../IR/MissingFeatures.h"
16+
#include "../../CodeGen/TargetInfo.h"
17+
#include "LoweringPrepareItaniumCXXABI.h"
18+
19+
#include <assert.h>
20+
21+
using cir::LoweringPrepareCXXABI;
22+
using cir::MissingFeatures;
23+
using cir::AArch64ABIKind;
24+
25+
using namespace mlir;
26+
using namespace mlir::cir;
27+
28+
namespace {
29+
class LoweringPrepareAArch64CXXABI : public LoweringPrepareItaniumCXXABI {
30+
public:
31+
LoweringPrepareAArch64CXXABI(AArch64ABIKind k) : Kind(k) {}
32+
mlir::Value lowerVAArg(CIRBaseBuilderTy &builder,
33+
mlir::cir::VAArgOp op) override;
34+
35+
private:
36+
AArch64ABIKind Kind;
37+
mlir::Value lowerAAPCSVAArg(CIRBaseBuilderTy &builder, mlir::cir::VAArgOp op);
38+
};
39+
} // namespace
40+
41+
LoweringPrepareCXXABI *
42+
LoweringPrepareCXXABI::createAArch64ABI(AArch64ABIKind k) {
43+
return new LoweringPrepareAArch64CXXABI(k);
44+
}
45+
46+
mlir::Value
47+
LoweringPrepareAArch64CXXABI::lowerAAPCSVAArg(CIRBaseBuilderTy &builder,
48+
mlir::cir::VAArgOp op) {
49+
auto loc = op->getLoc();
50+
auto valist = op->getOperand(0);
51+
auto opResTy = op.getType();
52+
// front end should not produce non-scalar type of VAArgOp
53+
bool isSupportedType =
54+
opResTy.isa<mlir::cir::IntType, mlir::cir::SingleType,
55+
mlir::cir::PointerType, mlir::cir::BoolType,
56+
mlir::cir::DoubleType>();
57+
58+
// Homogenous Aggregate type not supported and indirect arg
59+
// passing not supported yet. And for these supported types,
60+
// we should not have alignment greater than 8 problem.
61+
assert(isSupportedType);
62+
assert(!MissingFeatures::handleAArch64Indirect());
63+
64+
bool isFloatingType =
65+
opResTy.isa<mlir::cir::SingleType, mlir::cir::DoubleType>();
66+
67+
// The AArch64 va_list type and handling is specified in the Procedure Call
68+
// Standard, section B.4:
69+
//
70+
// struct {
71+
// void *__stack;
72+
// void *__gr_top;
73+
// void *__vr_top;
74+
// int __gr_offs;
75+
// int __vr_offs;
76+
// };
77+
auto curInsertionP = builder.saveInsertionPoint();
78+
auto currentBlock = builder.getInsertionBlock();
79+
auto boolTy = builder.getBoolTy();
80+
81+
auto maybeRegBlock = builder.createBlock(builder.getBlock()->getParent());
82+
auto inRegBlock = builder.createBlock(builder.getBlock()->getParent());
83+
auto onStackBlock = builder.createBlock(builder.getBlock()->getParent());
84+
85+
//=======================================
86+
// Find out where argument was passed
87+
//=======================================
88+
89+
// If v/gr_offs >= 0 we're already using the stack for this type of
90+
// argument. We don't want to keep updating reg_offs (in case it overflows,
91+
// though anyone passing 2GB of arguments, each at most 16 bytes, deserves
92+
// whatever they get).
93+
builder.restoreInsertionPoint(curInsertionP);
94+
// 3 is the field number of __gr_offs, 4 is the field number of __vr_offs
95+
auto offsP = builder.createGetMemberOp(loc, valist,
96+
isFloatingType ? "vr_offs" : "gr_offs",
97+
isFloatingType ? 4 : 3);
98+
auto offs = builder.create<mlir::cir::LoadOp>(loc, offsP);
99+
auto zeroValue = builder.create<mlir::cir::ConstantOp>(
100+
loc, offs.getType(), mlir::cir::IntAttr::get(offs.getType(), 0));
101+
auto cmpRes = builder.create<mlir::cir::CmpOp>(loc, boolTy, CmpOpKind::ge,
102+
offs, zeroValue);
103+
builder.create<mlir::cir::BrCondOp>(loc, cmpRes, onStackBlock, maybeRegBlock);
104+
auto newEndBlock = currentBlock->splitBlock(op);
105+
106+
// maybeRegBlock updates the gr_offs/vr_offs pointer for next call to va_arg
107+
// on this va_list. The fact that this is done unconditionally reflects the
108+
// fact that allocating an argument to the stack also uses up all the
109+
// remaining registers of the appropriate kind.
110+
builder.setInsertionPointToEnd(maybeRegBlock);
111+
auto boundaryValue = builder.create<mlir::cir::ConstantOp>(
112+
loc, offs.getType(),
113+
mlir::cir::IntAttr::get(offs.getType(), isFloatingType ? 16 : 8));
114+
auto newRegsOffs = builder.create<mlir::cir::BinOp>(
115+
loc, offs.getType(), mlir::cir::BinOpKind::Add, offs, boundaryValue);
116+
builder.createStore(loc, newRegsOffs, offsP);
117+
// Now we're in a position to decide whether this argument really was in
118+
// registers or not.
119+
auto maybeRegCmpRes = builder.create<mlir::cir::CmpOp>(
120+
loc, boolTy, CmpOpKind::le, newRegsOffs, zeroValue);
121+
builder.create<mlir::cir::BrCondOp>(loc, maybeRegCmpRes, inRegBlock,
122+
onStackBlock);
123+
124+
//=======================================
125+
// Argument was on the stack
126+
//=======================================
127+
builder.setInsertionPointToEnd(onStackBlock);
128+
auto stackP = builder.createGetMemberOp(loc, valist, "stack", 0);
129+
// On big-endian platforms, the value will be right-aligned in its stack slot.
130+
assert(!MissingFeatures::handleBigEndian());
131+
auto stack = builder.create<mlir::cir::LoadOp>(loc, stackP);
132+
auto ptrDiffTy =
133+
mlir::cir::IntType::get(builder.getContext(), 64, /*signed=*/false);
134+
auto eight = builder.create<mlir::cir::ConstantOp>(
135+
loc, ptrDiffTy, mlir::cir::IntAttr::get(ptrDiffTy, 8));
136+
auto i8Ty = IntegerType::get(builder.getContext(), 8);
137+
auto i8PtrTy = PointerType::get(builder.getContext(), i8Ty);
138+
auto castStack = builder.createBitcast(stack, i8PtrTy);
139+
// Write the new value of __stack for the next call to va_arg
140+
auto newStackAsi8Ptr = builder.create<mlir::cir::PtrStrideOp>(
141+
loc, castStack.getType(), castStack, eight);
142+
auto newStack = builder.createBitcast(newStackAsi8Ptr, stack.getType());
143+
builder.createStore(loc, newStack, stackP);
144+
builder.create<mlir::cir::BrOp>(loc, mlir::ValueRange{stack}, newEndBlock);
145+
146+
//=======================================
147+
// Argument was in registers
148+
//=======================================
149+
// Now we emit the code for if the argument was originally passed in
150+
// registers. First start the appropriate block:
151+
builder.setInsertionPointToEnd(inRegBlock);
152+
auto regTopP = builder.createGetMemberOp(loc, valist,
153+
isFloatingType ? "vr_top" : "gr_top",
154+
isFloatingType ? 2 : 1);
155+
auto regTop = builder.create<mlir::cir::LoadOp>(loc, regTopP);
156+
auto castRegTop = builder.createBitcast(regTop, i8PtrTy);
157+
// On big-endian platforms, the value will be right-aligned in its stack slot.
158+
assert(!MissingFeatures::handleBigEndian());
159+
auto resAsInt8P = builder.create<mlir::cir::PtrStrideOp>(
160+
loc, castRegTop.getType(), castRegTop, offs);
161+
auto resAsVoidP = builder.createBitcast(resAsInt8P, regTop.getType());
162+
builder.create<mlir::cir::BrOp>(loc, mlir::ValueRange{resAsVoidP},
163+
newEndBlock);
164+
165+
// generate additional instructions for end block
166+
builder.setInsertionPoint(op);
167+
newEndBlock->addArgument(stack.getType(), loc);
168+
auto resP = newEndBlock->getArgument(0);
169+
assert(resP.getType().isa<mlir::cir::PointerType>());
170+
auto opResPTy = PointerType::get(builder.getContext(), opResTy);
171+
auto castResP = builder.createBitcast(resP, opResPTy);
172+
auto res = builder.create<mlir::cir::LoadOp>(loc, castResP);
173+
return res.getResult();
174+
}
175+
176+
mlir::Value LoweringPrepareAArch64CXXABI::lowerVAArg(CIRBaseBuilderTy &builder,
177+
mlir::cir::VAArgOp op) {
178+
179+
if (Kind == AArch64ABIKind::AAPCS) {
180+
return lowerAAPCSVAArg(builder, op);
181+
}
182+
assert(0 && "unsupported AArch64 CXX ABI");
183+
}

clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#ifndef LLVM_CLANG_LIB_CIR_LOWERING_PREPARE_CXX_ABI_H
1616
#define LLVM_CLANG_LIB_CIR_LOWERING_PREPARE_CXX_ABI_H
1717

18+
#include "../../CodeGen/TargetInfo.h"
1819
#include "mlir/IR/Value.h"
1920
#include "clang/AST/ASTContext.h"
2021
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
@@ -25,7 +26,10 @@ namespace cir {
2526
class LoweringPrepareCXXABI {
2627
public:
2728
static LoweringPrepareCXXABI *createItaniumABI();
29+
static LoweringPrepareCXXABI *createAArch64ABI(AArch64ABIKind k);
2830

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

3135
virtual mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder,

0 commit comments

Comments
 (0)