Skip to content

Commit a5e55c2

Browse files
committed
AArch64 ABI lowering cir.var_arg, squashed related commits
1 parent 5730174 commit a5e55c2

11 files changed

+604
-32
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

+7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ 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; }
26+
static bool classifyArgumentTypeForAArch64() { return false; }
27+
static bool supportgetCoerceToTypeForAArch64() { return false; }
28+
static bool supportTySizeQueryForAArch64() { return false; }
29+
static bool supportTyAlignQueryForAArch64() { return false; }
30+
static bool supportisHomogeneousAggregateQueryForAArch64() { return false; }
2431

2532
// Address space related
2633
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

+32-6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "clang/AST/CharUnits.h"
1616
#include "clang/AST/Mangle.h"
1717
#include "clang/Basic/Module.h"
18+
#include "clang/Basic/TargetInfo.h"
1819
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
1920
#include "clang/CIR/Dialect/IR/CIRDialect.h"
2021
#include "clang/CIR/Dialect/Passes.h"
@@ -70,6 +71,7 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
7071

7172
void runOnOp(Operation *op);
7273
void lowerThreeWayCmpOp(CmpThreeWayOp op);
74+
void lowerVAArgOp(VAArgOp op);
7375
void lowerGlobalOp(GlobalOp op);
7476
void lowerDynamicCastOp(DynamicCastOp op);
7577
void lowerStdFindOp(StdFindOp op);
@@ -108,15 +110,24 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
108110

109111
void setASTContext(clang::ASTContext *c) {
110112
astCtx = c;
113+
auto abiStr = c->getTargetInfo().getABI();
111114
switch (c->getCXXABIKind()) {
112115
case clang::TargetCXXABI::GenericItanium:
116+
cxxABI.reset(::cir::LoweringPrepareCXXABI::createItaniumABI());
117+
break;
113118
case clang::TargetCXXABI::GenericAArch64:
114119
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());
120+
// TODO: This is temporary solution. ABIKind info should be
121+
// propagated from the targetInfo managed by ABI lowering
122+
// query system.
123+
assert(abiStr == "aapcs" || abiStr == "darwinpcs" ||
124+
abiStr == "aapcs-soft");
125+
cxxABI.reset(::cir::LoweringPrepareCXXABI::createAArch64ABI(
126+
abiStr == "aapcs"
127+
? ::cir::AArch64ABIKind::AAPCS
128+
: (abiStr == "darwinpccs" ? ::cir::AArch64ABIKind::DarwinPCS
129+
: ::cir::AArch64ABIKind::AAPCSSoft)));
118130
break;
119-
120131
default:
121132
llvm_unreachable("NYI");
122133
}
@@ -320,6 +331,18 @@ static void canonicalizeIntrinsicThreeWayCmp(CIRBaseBuilderTy &builder,
320331
op.erase();
321332
}
322333

334+
void LoweringPreparePass::lowerVAArgOp(VAArgOp op) {
335+
CIRBaseBuilderTy builder(getContext());
336+
builder.setInsertionPoint(op);
337+
338+
auto res = cxxABI->lowerVAArg(builder, op);
339+
if (res) {
340+
op.replaceAllUsesWith(res);
341+
op.erase();
342+
}
343+
return;
344+
}
345+
323346
void LoweringPreparePass::lowerThreeWayCmpOp(CmpThreeWayOp op) {
324347
CIRBaseBuilderTy builder(getContext());
325348
builder.setInsertionPointAfter(op);
@@ -603,6 +626,8 @@ void LoweringPreparePass::lowerIterEndOp(IterEndOp op) {
603626
void LoweringPreparePass::runOnOp(Operation *op) {
604627
if (auto threeWayCmp = dyn_cast<CmpThreeWayOp>(op)) {
605628
lowerThreeWayCmpOp(threeWayCmp);
629+
} else if (auto vaArgOp = dyn_cast<VAArgOp>(op)) {
630+
lowerVAArgOp(vaArgOp);
606631
} else if (auto getGlobal = dyn_cast<GlobalOp>(op)) {
607632
lowerGlobalOp(getGlobal);
608633
} else if (auto dynamicCast = dyn_cast<DynamicCastOp>(op)) {
@@ -635,8 +660,9 @@ void LoweringPreparePass::runOnOperation() {
635660

636661
SmallVector<Operation *> opsToTransform;
637662
op->walk([&](Operation *op) {
638-
if (isa<CmpThreeWayOp, GlobalOp, DynamicCastOp, StdFindOp, IterEndOp,
639-
IterBeginOp, ArrayCtor, ArrayDtor, mlir::cir::FuncOp>(op))
663+
if (isa<CmpThreeWayOp, VAArgOp, GlobalOp, DynamicCastOp, StdFindOp,
664+
IterEndOp, IterBeginOp, ArrayCtor, ArrayDtor, mlir::cir::FuncOp>(
665+
op))
640666
opsToTransform.push_back(op);
641667
});
642668

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
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+
#include "clang/CIR/Dialect/IR/CIRTypes.h"
18+
19+
#include <assert.h>
20+
21+
using cir::AArch64ABIKind;
22+
23+
namespace {
24+
class LoweringPrepareAArch64CXXABI : public LoweringPrepareItaniumCXXABI {
25+
public:
26+
LoweringPrepareAArch64CXXABI(AArch64ABIKind k) : Kind(k) {}
27+
mlir::Value lowerVAArg(cir::CIRBaseBuilderTy &builder,
28+
mlir::cir::VAArgOp op) override;
29+
30+
private:
31+
AArch64ABIKind Kind;
32+
mlir::Value lowerAAPCSVAArg(cir::CIRBaseBuilderTy &builder,
33+
mlir::cir::VAArgOp op);
34+
bool isDarwinPCS() const { return Kind == AArch64ABIKind::DarwinPCS; }
35+
mlir::Value lowerMSVAArg(cir::CIRBaseBuilderTy &builder,
36+
mlir::cir::VAArgOp op) {
37+
llvm_unreachable("MSVC ABI not supported yet");
38+
}
39+
mlir::Value lowerDarwinVAArg(cir::CIRBaseBuilderTy &builder,
40+
mlir::cir::VAArgOp op) {
41+
llvm_unreachable("Darwin ABI not supported yet");
42+
}
43+
};
44+
} // namespace
45+
46+
cir::LoweringPrepareCXXABI *
47+
cir::LoweringPrepareCXXABI::createAArch64ABI(AArch64ABIKind k) {
48+
return new LoweringPrepareAArch64CXXABI(k);
49+
}
50+
51+
mlir::Value
52+
LoweringPrepareAArch64CXXABI::lowerAAPCSVAArg(cir::CIRBaseBuilderTy &builder,
53+
mlir::cir::VAArgOp op) {
54+
auto loc = op->getLoc();
55+
auto valist = op->getOperand(0);
56+
auto opResTy = op.getType();
57+
// front end should not produce non-scalar type of VAArgOp
58+
bool isSupportedType =
59+
opResTy.isa<mlir::cir::IntType, mlir::cir::SingleType,
60+
mlir::cir::PointerType, mlir::cir::BoolType,
61+
mlir::cir::DoubleType, mlir::cir::ArrayType>();
62+
63+
// Homogenous Aggregate type not supported and indirect arg
64+
// passing not supported yet. And for these supported types,
65+
// we should not have alignment greater than 8 problem.
66+
assert(isSupportedType);
67+
assert(!cir::MissingFeatures::classifyArgumentTypeForAArch64());
68+
// indirect arg passing would expect one more level of pointer dereference.
69+
assert(!cir::MissingFeatures::handleAArch64Indirect());
70+
assert(!cir::MissingFeatures::supportgetCoerceToTypeForAArch64());
71+
// we don't convert to LLVM Type here as we are lowering to CIR here.
72+
// so baseTy is the just type of the result of va_arg.
73+
// but it depends on arg type indirectness and coercion defined by ABI.
74+
auto baseTy = opResTy;
75+
76+
if (baseTy.isa<mlir::cir::ArrayType>()) {
77+
llvm_unreachable("ArrayType VAArg loweing NYI");
78+
}
79+
// numRegs may not be 1 if ArrayType is supported.
80+
unsigned numRegs = 1;
81+
82+
if (Kind == AArch64ABIKind::AAPCSSoft) {
83+
llvm_unreachable("AAPCSSoft cir.var_arg lowering NYI");
84+
}
85+
bool IsFPR = mlir::cir::isAnyFloatingPointType(opResTy);
86+
87+
// The AArch64 va_list type and handling is specified in the Procedure Call
88+
// Standard, section B.4:
89+
//
90+
// struct {
91+
// void *__stack;
92+
// void *__gr_top;
93+
// void *__vr_top;
94+
// int __gr_offs;
95+
// int __vr_offs;
96+
// };
97+
auto curInsertionP = builder.saveInsertionPoint();
98+
auto currentBlock = builder.getInsertionBlock();
99+
auto boolTy = builder.getBoolTy();
100+
101+
auto maybeRegBlock = builder.createBlock(builder.getBlock()->getParent());
102+
auto inRegBlock = builder.createBlock(builder.getBlock()->getParent());
103+
auto onStackBlock = builder.createBlock(builder.getBlock()->getParent());
104+
105+
//=======================================
106+
// Find out where argument was passed
107+
//=======================================
108+
109+
// If v/gr_offs >= 0 we're already using the stack for this type of
110+
// argument. We don't want to keep updating regOffs (in case it overflows,
111+
// though anyone passing 2GB of arguments, each at most 16 bytes, deserves
112+
// whatever they get).
113+
114+
assert(!cir::MissingFeatures::handleAArch64Indirect());
115+
assert(!cir::MissingFeatures::supportTySizeQueryForAArch64());
116+
assert(!cir::MissingFeatures::supportTyAlignQueryForAArch64());
117+
// indirectness, type size and type alignment all
118+
// decide regSize, but they are all ABI defined
119+
// thus need ABI lowering query system.
120+
int regSize = 8;
121+
int regTopIndex;
122+
mlir::Value regOffsP;
123+
mlir::cir::LoadOp regOffs;
124+
125+
builder.restoreInsertionPoint(curInsertionP);
126+
// 3 is the field number of __gr_offs, 4 is the field number of __vr_offs
127+
if (!IsFPR) {
128+
regOffsP = builder.createGetMemberOp(loc, valist, "gr_offs", 3);
129+
regOffs = builder.create<mlir::cir::LoadOp>(loc, regOffsP);
130+
regTopIndex = 1;
131+
regSize = llvm::alignTo(regSize, 8);
132+
} else {
133+
regOffsP = builder.createGetMemberOp(loc, valist, "vr_offs", 4);
134+
regOffs = builder.create<mlir::cir::LoadOp>(loc, regOffsP);
135+
regTopIndex = 2;
136+
regSize = 16 * numRegs;
137+
}
138+
139+
//=======================================
140+
// Find out where argument was passed
141+
//=======================================
142+
143+
// If regOffs >= 0 we're already using the stack for this type of
144+
// argument. We don't want to keep updating regOffs (in case it overflows,
145+
// though anyone passing 2GB of arguments, each at most 16 bytes, deserves
146+
// whatever they get).
147+
auto zeroValue = builder.create<mlir::cir::ConstantOp>(
148+
loc, regOffs.getType(), mlir::cir::IntAttr::get(regOffs.getType(), 0));
149+
auto usingStack = builder.create<mlir::cir::CmpOp>(
150+
loc, boolTy, mlir::cir::CmpOpKind::ge, regOffs, zeroValue);
151+
builder.create<mlir::cir::BrCondOp>(loc, usingStack, onStackBlock,
152+
maybeRegBlock);
153+
154+
auto contBlock = currentBlock->splitBlock(op);
155+
156+
// Otherwise, at least some kind of argument could go in these registers, the
157+
// question is whether this particular type is too big.
158+
builder.setInsertionPointToEnd(maybeRegBlock);
159+
160+
// Integer arguments may need to correct register alignment (for example a
161+
// "struct { __int128 a; };" gets passed in x_2N, x_{2N+1}). In this case we
162+
// align __gr_offs to calculate the potential address.
163+
if (!IsFPR) {
164+
assert(!cir::MissingFeatures::handleAArch64Indirect());
165+
assert(!cir::MissingFeatures::supportTyAlignQueryForAArch64());
166+
}
167+
168+
// Update the gr_offs/vr_offs pointer for next call to va_arg on this va_list.
169+
// The fact that this is done unconditionally reflects the fact that
170+
// allocating an argument to the stack also uses up all the remaining
171+
// registers of the appropriate kind.
172+
auto regSizeValue = builder.create<mlir::cir::ConstantOp>(
173+
loc, regOffs.getType(),
174+
mlir::cir::IntAttr::get(regOffs.getType(), regSize));
175+
auto newOffset = builder.create<mlir::cir::BinOp>(
176+
loc, regOffs.getType(), mlir::cir::BinOpKind::Add, regOffs, regSizeValue);
177+
builder.createStore(loc, newOffset, regOffsP);
178+
// Now we're in a position to decide whether this argument really was in
179+
// registers or not.
180+
auto inRegs = builder.create<mlir::cir::CmpOp>(
181+
loc, boolTy, mlir::cir::CmpOpKind::le, newOffset, zeroValue);
182+
builder.create<mlir::cir::BrCondOp>(loc, inRegs, inRegBlock, onStackBlock);
183+
184+
//=======================================
185+
// Argument was in registers
186+
//=======================================
187+
// Now we emit the code for if the argument was originally passed in
188+
// registers. First start the appropriate block:
189+
builder.setInsertionPointToEnd(inRegBlock);
190+
auto regTopP = builder.createGetMemberOp(
191+
loc, valist, IsFPR ? "vr_top" : "gr_top", regTopIndex);
192+
auto regTop = builder.create<mlir::cir::LoadOp>(loc, regTopP);
193+
auto i8Ty = mlir::IntegerType::get(builder.getContext(), 8);
194+
auto i8PtrTy = mlir::cir::PointerType::get(builder.getContext(), i8Ty);
195+
auto castRegTop = builder.createBitcast(regTop, i8PtrTy);
196+
// On big-endian platforms, the value will be right-aligned in its stack slot.
197+
// and we also need to think about other ABI lowering concerns listed below.
198+
assert(!cir::MissingFeatures::handleBigEndian());
199+
assert(!cir::MissingFeatures::handleAArch64Indirect());
200+
assert(!cir::MissingFeatures::supportisHomogeneousAggregateQueryForAArch64());
201+
assert(!cir::MissingFeatures::supportTySizeQueryForAArch64());
202+
assert(!cir::MissingFeatures::supportTyAlignQueryForAArch64());
203+
204+
auto resAsInt8P = builder.create<mlir::cir::PtrStrideOp>(
205+
loc, castRegTop.getType(), castRegTop, regOffs);
206+
auto resAsVoidP = builder.createBitcast(resAsInt8P, regTop.getType());
207+
builder.create<mlir::cir::BrOp>(loc, mlir::ValueRange{resAsVoidP}, contBlock);
208+
209+
//=======================================
210+
// Argument was on the stack
211+
//=======================================
212+
builder.setInsertionPointToEnd(onStackBlock);
213+
auto stackP = builder.createGetMemberOp(loc, valist, "stack", 0);
214+
215+
auto onStackPtr = builder.create<mlir::cir::LoadOp>(loc, stackP);
216+
auto ptrDiffTy =
217+
mlir::cir::IntType::get(builder.getContext(), 64, /*signed=*/false);
218+
219+
// On big-endian platforms, the value will be right-aligned in its stack slot
220+
// Also, the consideration involves type size and alignment, arg indirectness
221+
// which are all ABI defined thus need ABI lowering query system.
222+
// The implementation we have now supports most common cases which assumes
223+
// no indirectness, no alignment greater than 8, and little endian.
224+
assert(!cir::MissingFeatures::handleBigEndian());
225+
assert(!cir::MissingFeatures::handleAArch64Indirect());
226+
assert(!cir::MissingFeatures::supportTyAlignQueryForAArch64());
227+
assert(!cir::MissingFeatures::supportTySizeQueryForAArch64());
228+
229+
auto eight = builder.create<mlir::cir::ConstantOp>(
230+
loc, ptrDiffTy, mlir::cir::IntAttr::get(ptrDiffTy, 8));
231+
auto castStack = builder.createBitcast(onStackPtr, i8PtrTy);
232+
// Write the new value of __stack for the next call to va_arg
233+
auto newStackAsi8Ptr = builder.create<mlir::cir::PtrStrideOp>(
234+
loc, castStack.getType(), castStack, eight);
235+
auto newStack = builder.createBitcast(newStackAsi8Ptr, onStackPtr.getType());
236+
builder.createStore(loc, newStack, stackP);
237+
builder.create<mlir::cir::BrOp>(loc, mlir::ValueRange{onStackPtr}, contBlock);
238+
239+
// generate additional instructions for end block
240+
builder.setInsertionPoint(op);
241+
contBlock->addArgument(onStackPtr.getType(), loc);
242+
auto resP = contBlock->getArgument(0);
243+
assert(resP.getType().isa<mlir::cir::PointerType>());
244+
auto opResPTy = mlir::cir::PointerType::get(builder.getContext(), opResTy);
245+
auto castResP = builder.createBitcast(resP, opResPTy);
246+
auto res = builder.create<mlir::cir::LoadOp>(loc, castResP);
247+
// there would be another level of ptr dereference if indirect arg passing
248+
assert(!cir::MissingFeatures::handleAArch64Indirect());
249+
return res.getResult();
250+
}
251+
252+
mlir::Value
253+
LoweringPrepareAArch64CXXABI::lowerVAArg(cir::CIRBaseBuilderTy &builder,
254+
mlir::cir::VAArgOp op) {
255+
return Kind == AArch64ABIKind::Win64 ? lowerMSVAArg(builder, op)
256+
: isDarwinPCS() ? lowerDarwinVAArg(builder, op)
257+
: lowerAAPCSVAArg(builder, op);
258+
}

0 commit comments

Comments
 (0)