Skip to content

Commit 2e48c55

Browse files
committed
AArch64 ABI lowering cir.var_arg, squashed related commits
1 parent 3e4d5e6 commit 2e48c55

11 files changed

+513
-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/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

+23-6
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
7070

7171
void runOnOp(Operation *op);
7272
void lowerThreeWayCmpOp(CmpThreeWayOp op);
73+
void lowerVAArgOp(VAArgOp op);
7374
void lowerGlobalOp(GlobalOp op);
7475
void lowerDynamicCastOp(DynamicCastOp op);
7576
void lowerStdFindOp(StdFindOp op);
@@ -108,13 +109,14 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
108109

109110
void setASTContext(clang::ASTContext *c) {
110111
astCtx = c;
111-
switch (c->getCXXABIKind()) {
112+
auto kind = c->getCXXABIKind();
113+
switch (kind) {
112114
case clang::TargetCXXABI::GenericItanium:
115+
cxxABI.reset(::cir::LoweringPrepareCXXABI::createItaniumABI());
116+
break;
113117
case clang::TargetCXXABI::GenericAArch64:
114118
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());
119+
cxxABI.reset(::cir::LoweringPrepareCXXABI::createAArch64ABI(kind));
118120
break;
119121

120122
default:
@@ -320,6 +322,18 @@ static void canonicalizeIntrinsicThreeWayCmp(CIRBaseBuilderTy &builder,
320322
op.erase();
321323
}
322324

325+
void LoweringPreparePass::lowerVAArgOp(VAArgOp op) {
326+
CIRBaseBuilderTy builder(getContext());
327+
builder.setInsertionPoint(op);
328+
329+
auto res = cxxABI->lowerVAArg(builder, op);
330+
if (res) {
331+
op.replaceAllUsesWith(res);
332+
op.erase();
333+
}
334+
return;
335+
}
336+
323337
void LoweringPreparePass::lowerThreeWayCmpOp(CmpThreeWayOp op) {
324338
CIRBaseBuilderTy builder(getContext());
325339
builder.setInsertionPointAfter(op);
@@ -603,6 +617,8 @@ void LoweringPreparePass::lowerIterEndOp(IterEndOp op) {
603617
void LoweringPreparePass::runOnOp(Operation *op) {
604618
if (auto threeWayCmp = dyn_cast<CmpThreeWayOp>(op)) {
605619
lowerThreeWayCmpOp(threeWayCmp);
620+
} else if (auto vaArgOp = dyn_cast<VAArgOp>(op)) {
621+
lowerVAArgOp(vaArgOp);
606622
} else if (auto getGlobal = dyn_cast<GlobalOp>(op)) {
607623
lowerGlobalOp(getGlobal);
608624
} else if (auto dynamicCast = dyn_cast<DynamicCastOp>(op)) {
@@ -635,8 +651,9 @@ void LoweringPreparePass::runOnOperation() {
635651

636652
SmallVector<Operation *> opsToTransform;
637653
op->walk([&](Operation *op) {
638-
if (isa<CmpThreeWayOp, GlobalOp, DynamicCastOp, StdFindOp, IterEndOp,
639-
IterBeginOp, ArrayCtor, ArrayDtor, mlir::cir::FuncOp>(op))
654+
if (isa<CmpThreeWayOp, VAArgOp, GlobalOp, DynamicCastOp, StdFindOp,
655+
IterEndOp, IterBeginOp, ArrayCtor, ArrayDtor, mlir::cir::FuncOp>(
656+
op))
640657
opsToTransform.push_back(op);
641658
});
642659

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

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

+3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ namespace cir {
2525
class LoweringPrepareCXXABI {
2626
public:
2727
static LoweringPrepareCXXABI *createItaniumABI();
28+
static LoweringPrepareCXXABI *createAArch64ABI(clang::TargetCXXABI::Kind k);
2829

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

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

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

+15-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
//====- LoweringPrepareItaniumCXXABI.h - Itanium ABI specific code --------===//
1+
//====- LoweringPrepareItaniumCXXABI.cpp - Itanium ABI specific code
2+
//--------===//
23
//
34
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45
// See https://llvm.org/LICENSE.txt for license information.
@@ -11,8 +12,8 @@
1112
//
1213
//===----------------------------------------------------------------------===//
1314

15+
#include "LoweringPrepareItaniumCXXABI.h"
1416
#include "../IR/MissingFeatures.h"
15-
#include "LoweringPrepareCXXABI.h"
1617
#include "mlir/IR/BuiltinAttributes.h"
1718
#include "mlir/IR/Value.h"
1819
#include "mlir/IR/ValueRange.h"
@@ -21,18 +22,11 @@
2122
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
2223
#include "clang/CIR/Dialect/IR/CIRDialect.h"
2324

24-
using namespace cir;
25-
26-
namespace {
27-
28-
class LoweringPrepareItaniumCXXABI : public LoweringPrepareCXXABI {
29-
public:
30-
mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder,
31-
clang::ASTContext &astCtx,
32-
mlir::cir::DynamicCastOp op) override;
33-
};
34-
35-
} // namespace
25+
using cir::CIRBaseBuilderTy;
26+
using cir::LoweringPrepareCXXABI;
27+
using cir::MissingFeatures;
28+
using namespace mlir;
29+
using namespace mlir::cir;
3630

3731
LoweringPrepareCXXABI *LoweringPrepareCXXABI::createItaniumABI() {
3832
return new LoweringPrepareItaniumCXXABI();
@@ -169,3 +163,10 @@ LoweringPrepareItaniumCXXABI::lowerDynamicCast(CIRBaseBuilderTy &builder,
169163
})
170164
.getResult();
171165
}
166+
167+
mlir::Value LoweringPrepareItaniumCXXABI::lowerVAArg(CIRBaseBuilderTy &builder,
168+
mlir::cir::VAArgOp op) {
169+
// There is no generic cir lowering for var_arg, here we just return empty
170+
// so preserve cir.var_arg and leave responsibility of lowering to LowerToLLVM
171+
llvm_unreachable("NYI");
172+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//====- LoweringPrepareItaniumCXXABI.h - Itanium ABI specific code --------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file provides Itanium C++ ABI specific code that is used during LLVMIR
10+
// lowering prepare.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "LoweringPrepareCXXABI.h"
15+
16+
using cir::CIRBaseBuilderTy;
17+
18+
class LoweringPrepareItaniumCXXABI : public cir::LoweringPrepareCXXABI {
19+
public:
20+
mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder,
21+
clang::ASTContext &astCtx,
22+
mlir::cir::DynamicCastOp op) override;
23+
mlir::Value lowerVAArg(CIRBaseBuilderTy &builder,
24+
mlir::cir::VAArgOp op) override;
25+
};

0 commit comments

Comments
 (0)