Skip to content

Commit 0a6e761

Browse files
committed
AArch64 ABI lowering cir.var_arg, squashed related commits
1 parent f690ca9 commit 0a6e761

11 files changed

+479
-21
lines changed

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

+13
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,19 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
244244
return createCast(mlir::cir::CastKind::int_to_ptr, src, newTy);
245245
}
246246

247+
mlir::Value createGetMemberOp(mlir::Location &loc, mlir::Value structPtr,
248+
const char *fldName, unsigned idx) {
249+
250+
assert(structPtr.getType().isa<mlir::cir::PointerType>());
251+
auto structBaseTy =
252+
structPtr.getType().cast<mlir::cir::PointerType>().getPointee();
253+
assert(structBaseTy.isa<mlir::cir::StructType>());
254+
auto fldTy = structBaseTy.cast<mlir::cir::StructType>().getMembers()[idx];
255+
auto fldPtrTy = ::mlir::cir::PointerType::get(getContext(), fldTy);
256+
return create<mlir::cir::GetMemberOp>(loc, fldPtrTy, structPtr, fldName,
257+
idx);
258+
}
259+
247260
mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) {
248261
return createCast(mlir::cir::CastKind::ptr_to_int, src, newTy);
249262
}

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

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

2526
// Address space related
2627
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);
@@ -601,6 +615,8 @@ void LoweringPreparePass::lowerIterEndOp(IterEndOp op) {
601615
void LoweringPreparePass::runOnOp(Operation *op) {
602616
if (auto threeWayCmp = dyn_cast<CmpThreeWayOp>(op)) {
603617
lowerThreeWayCmpOp(threeWayCmp);
618+
} else if (auto vaArgOp = dyn_cast<VAArgOp>(op)) {
619+
lowerVAArgOp(vaArgOp);
604620
} else if (auto getGlobal = dyn_cast<GlobalOp>(op)) {
605621
lowerGlobalOp(getGlobal);
606622
} else if (auto dynamicCast = dyn_cast<DynamicCastOp>(op)) {
@@ -633,8 +649,9 @@ void LoweringPreparePass::runOnOperation() {
633649

634650
SmallVector<Operation *> opsToTransform;
635651
op->walk([&](Operation *op) {
636-
if (isa<CmpThreeWayOp, GlobalOp, DynamicCastOp, StdFindOp, IterEndOp,
637-
IterBeginOp, ArrayCtor, ArrayDtor, mlir::cir::FuncOp>(op))
652+
if (isa<CmpThreeWayOp, VAArgOp, GlobalOp, DynamicCastOp, StdFindOp,
653+
IterEndOp, IterBeginOp, ArrayCtor, ArrayDtor, mlir::cir::FuncOp>(
654+
op))
638655
opsToTransform.push_back(op);
639656
});
640657

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

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

+3
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@ namespace cir {
2424
class LoweringPrepareCXXABI {
2525
public:
2626
static LoweringPrepareCXXABI *createItaniumABI();
27+
static LoweringPrepareCXXABI *createAArch64ABI(clang::TargetCXXABI::Kind k);
2728

2829
virtual mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder,
2930
mlir::cir::DynamicCastOp op) = 0;
3031

32+
virtual mlir::Value lowerVAArg(CIRBaseBuilderTy &builder,
33+
mlir::cir::VAArgOp op) = 0;
3134
virtual ~LoweringPrepareCXXABI() {}
3235
};
3336

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

+14-13
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,26 +12,20 @@
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"
1920
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
2021
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
2122
#include "clang/CIR/Dialect/IR/CIRDialect.h"
2223

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

3530
LoweringPrepareCXXABI *LoweringPrepareCXXABI::createItaniumABI() {
3631
return new LoweringPrepareItaniumCXXABI();
@@ -115,3 +110,9 @@ LoweringPrepareItaniumCXXABI::lowerDynamicCast(CIRBaseBuilderTy &builder,
115110
})
116111
.getResult();
117112
}
113+
114+
mlir::Value LoweringPrepareItaniumCXXABI::lowerVAArg(CIRBaseBuilderTy &builder,
115+
mlir::cir::VAArgOp op) {
116+
// TODO: implement va_arg for more generic Itanium ABI.
117+
return mlir::Value();
118+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
mlir::cir::DynamicCastOp op) override;
22+
mlir::Value lowerVAArg(CIRBaseBuilderTy &builder,
23+
mlir::cir::VAArgOp op) override;
24+
};

0 commit comments

Comments
 (0)