Skip to content

Commit c566b19

Browse files
authored
[CIR] Make use of !invariant.group metadata for const allocas (#1159)
This PR updates the LLVM lowering part of load and stores to const allocas and makes use of the !invariant.group metadata in the result LLVM IR. The HoistAlloca pass is also updated. The const flag on a hoisted alloca is removed for now since their uses are not always invariants. Will update in later PRs to teach their invariants.
1 parent 7fb608d commit c566b19

File tree

4 files changed

+102
-28
lines changed

4 files changed

+102
-28
lines changed

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,18 @@ static void process(cir::FuncOp func) {
4848

4949
mlir::Operation *insertPoint = &*entryBlock.begin();
5050

51-
for (auto alloca : allocas)
51+
for (auto alloca : allocas) {
5252
alloca->moveBefore(insertPoint);
53+
if (alloca.getConstant()) {
54+
// Hoisted alloca may come from the body of a loop, in which case the
55+
// stack slot is re-used by multiple objects alive in different iterations
56+
// of the loop. In theory, each of these objects are still constant within
57+
// their lifetimes, but currently we're not emitting metadata to further
58+
// describe this. So for now let's behave conservatively and remove the
59+
// const flag on nested allocas when hoisting them.
60+
alloca.setConstant(false);
61+
}
62+
}
5363
}
5464

5565
void HoistAllocasPass::runOnOperation() {

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

+27-8
Original file line numberDiff line numberDiff line change
@@ -959,8 +959,7 @@ mlir::LogicalResult CIRToLLVMVTTAddrPointOpLowering::matchAndRewrite(
959959

960960
if (op.getSymAddr()) {
961961
if (op.getOffset() == 0) {
962-
rewriter.replaceAllUsesWith(op, llvmAddr);
963-
rewriter.eraseOp(op);
962+
rewriter.replaceOp(op, {llvmAddr});
964963
return mlir::success();
965964
}
966965

@@ -1490,11 +1489,21 @@ mlir::LogicalResult CIRToLLVMLoadOpLowering::matchAndRewrite(
14901489
alignment = *alignOpt;
14911490
}
14921491

1493-
// TODO: nontemporal, invariant, syncscope.
1492+
auto invariant = false;
1493+
// Under -O1 or higher optimization levels, add the invariant metadata if the
1494+
// load operation loads from a constant object.
1495+
if (lowerMod &&
1496+
lowerMod->getContext().getCodeGenOpts().OptimizationLevel > 0) {
1497+
auto addrAllocaOp =
1498+
mlir::dyn_cast_if_present<cir::AllocaOp>(op.getAddr().getDefiningOp());
1499+
invariant = addrAllocaOp && addrAllocaOp.getConstant();
1500+
}
1501+
1502+
// TODO: nontemporal, syncscope.
14941503
rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
14951504
op, llvmTy, adaptor.getAddr(), /* alignment */ alignment,
14961505
op.getIsVolatile(), /* nontemporal */ false,
1497-
/* invariant */ false, /* invariantGroup */ false, ordering);
1506+
/* invariant */ false, /* invariantGroup */ invariant, ordering);
14981507
return mlir::LogicalResult::success();
14991508
}
15001509

@@ -1515,10 +1524,20 @@ mlir::LogicalResult CIRToLLVMStoreOpLowering::matchAndRewrite(
15151524
alignment = *alignOpt;
15161525
}
15171526

1527+
auto invariant = false;
1528+
// Under -O1 or higher optimization levels, add the invariant metadata if the
1529+
// store operation stores to a constant object.
1530+
if (lowerMod &&
1531+
lowerMod->getContext().getCodeGenOpts().OptimizationLevel > 0) {
1532+
auto addrAllocaOp =
1533+
mlir::dyn_cast_if_present<cir::AllocaOp>(op.getAddr().getDefiningOp());
1534+
invariant = addrAllocaOp && addrAllocaOp.getConstant();
1535+
}
1536+
15181537
// TODO: nontemporal, syncscope.
15191538
rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
15201539
op, adaptor.getValue(), adaptor.getAddr(), alignment, op.getIsVolatile(),
1521-
/* nontemporal */ false, /* invariantGroup */ false, ordering);
1540+
/* nontemporal */ false, /* invariantGroup */ invariant, ordering);
15221541
return mlir::LogicalResult::success();
15231542
}
15241543

@@ -3887,7 +3906,9 @@ void populateCIRToLLVMConversionPatterns(
38873906
CIRToLLVMConstantOpLowering,
38883907
CIRToLLVMDerivedDataMemberOpLowering,
38893908
CIRToLLVMGetRuntimeMemberOpLowering,
3890-
CIRToLLVMGlobalOpLowering
3909+
CIRToLLVMGlobalOpLowering,
3910+
CIRToLLVMLoadOpLowering,
3911+
CIRToLLVMStoreOpLowering
38913912
// clang-format on
38923913
>(converter, patterns.getContext(), lowerModule);
38933914
patterns.add<
@@ -3938,7 +3959,6 @@ void populateCIRToLLVMConversionPatterns(
39383959
CIRToLLVMIsConstantOpLowering,
39393960
CIRToLLVMIsFPClassOpLowering,
39403961
CIRToLLVMLLVMIntrinsicCallOpLowering,
3941-
CIRToLLVMLoadOpLowering,
39423962
CIRToLLVMMemChrOpLowering,
39433963
CIRToLLVMMemCpyInlineOpLowering,
39443964
CIRToLLVMMemCpyOpLowering,
@@ -3958,7 +3978,6 @@ void populateCIRToLLVMConversionPatterns(
39583978
CIRToLLVMShiftOpLowering,
39593979
CIRToLLVMSignBitOpLowering,
39603980
CIRToLLVMStackSaveOpLowering,
3961-
CIRToLLVMStoreOpLowering,
39623981
CIRToLLVMSwitchFlatOpLowering,
39633982
CIRToLLVMThrowOpLowering,
39643983
CIRToLLVMTrapOpLowering,

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h

+12-2
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,13 @@ class CIRToLLVMAllocaOpLowering
301301
};
302302

303303
class CIRToLLVMLoadOpLowering : public mlir::OpConversionPattern<cir::LoadOp> {
304+
cir::LowerModule *lowerMod;
305+
304306
public:
305-
using mlir::OpConversionPattern<cir::LoadOp>::OpConversionPattern;
307+
CIRToLLVMLoadOpLowering(const mlir::TypeConverter &typeConverter,
308+
mlir::MLIRContext *context,
309+
cir::LowerModule *lowerModule)
310+
: OpConversionPattern(typeConverter, context), lowerMod(lowerModule) {}
306311

307312
mlir::LogicalResult
308313
matchAndRewrite(cir::LoadOp op, OpAdaptor,
@@ -311,8 +316,13 @@ class CIRToLLVMLoadOpLowering : public mlir::OpConversionPattern<cir::LoadOp> {
311316

312317
class CIRToLLVMStoreOpLowering
313318
: public mlir::OpConversionPattern<cir::StoreOp> {
319+
cir::LowerModule *lowerMod;
320+
314321
public:
315-
using mlir::OpConversionPattern<cir::StoreOp>::OpConversionPattern;
322+
CIRToLLVMStoreOpLowering(const mlir::TypeConverter &typeConverter,
323+
mlir::MLIRContext *context,
324+
cir::LowerModule *lowerModule)
325+
: OpConversionPattern(typeConverter, context), lowerMod(lowerModule) {}
316326

317327
mlir::LogicalResult
318328
matchAndRewrite(cir::StoreOp op, OpAdaptor,
+52-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2-
// RUN: FileCheck --input-file=%t.cir %s
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir --check-prefix=CIR %s
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -fclangir -emit-llvm %s -o %t.ll
4+
// RUN: FileCheck --input-file=%t.ll --check-prefix=LLVM %s
35

46
int produce_int();
57
void blackbox(const int &);
@@ -8,33 +10,33 @@ void local_const_int() {
810
const int x = produce_int();
911
}
1012

11-
// CHECK-LABEL: @_Z15local_const_intv
12-
// CHECK: %{{.+}} = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init, const]
13-
// CHECK: }
13+
// CIR-LABEL: @_Z15local_const_intv
14+
// CIR: %{{.+}} = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init, const]
15+
// CIR: }
1416

1517
void param_const_int(const int x) {}
1618

17-
// CHECK-LABEL: @_Z15param_const_inti
18-
// CHECK: %{{.+}} = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init, const]
19-
// CHECK: }
19+
// CIR-LABEL: @_Z15param_const_inti
20+
// CIR: %{{.+}} = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init, const]
21+
// CIR: }
2022

2123
void local_constexpr_int() {
2224
constexpr int x = 42;
2325
blackbox(x);
2426
}
2527

26-
// CHECK-LABEL: @_Z19local_constexpr_intv
27-
// CHECK: %{{.+}} = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init, const]
28-
// CHECK: }
28+
// CIR-LABEL: @_Z19local_constexpr_intv
29+
// CIR: %{{.+}} = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init, const]
30+
// CIR: }
2931

3032
void local_reference() {
3133
int x = 0;
3234
int &r = x;
3335
}
3436

35-
// CHECK-LABEL: @_Z15local_referencev
36-
// CHECK: %{{.+}} = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["r", init, const]
37-
// CHECK: }
37+
// CIR-LABEL: @_Z15local_referencev
38+
// CIR: %{{.+}} = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["r", init, const]
39+
// CIR: }
3840

3941
struct Foo {
4042
int a;
@@ -47,6 +49,39 @@ void local_const_struct() {
4749
const Foo x = produce_foo();
4850
}
4951

50-
// CHECK-LABEL: @_Z18local_const_structv
51-
// CHECK: %{{.+}} = cir.alloca !ty_Foo, !cir.ptr<!ty_Foo>, ["x", init, const]
52-
// CHECK: }
52+
// CIR-LABEL: @_Z18local_const_structv
53+
// CIR: %{{.+}} = cir.alloca !ty_Foo, !cir.ptr<!ty_Foo>, ["x", init, const]
54+
// CIR: }
55+
56+
[[clang::optnone]]
57+
int local_const_load_store() {
58+
const int x = produce_int();
59+
int y = x;
60+
return y;
61+
}
62+
63+
// CIR-LABEL: @_Z22local_const_load_storev
64+
// CIR: %{{.+}} = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init, const] {alignment = 4 : i64}
65+
// CIR: }
66+
67+
// LLVM-LABEL: @_Z22local_const_load_storev
68+
// LLVM: %[[#INIT:]] = call i32 @_Z11produce_intv()
69+
// LLVM-NEXT: store i32 %[[#INIT]], ptr %[[#SLOT:]], align 4, !invariant.group !{{.+}}
70+
// LLVM-NEXT: %{{.+}} = load i32, ptr %[[#SLOT]], align 4, !invariant.group !{{.+}}
71+
// LLVM: }
72+
73+
int local_const_optimize() {
74+
const int x = produce_int();
75+
blackbox(x);
76+
blackbox(x);
77+
return x;
78+
}
79+
80+
// LLVM-LABEL: @_Z20local_const_optimizev()
81+
// LLVM-NEXT: %[[#slot:]] = alloca i32, align 4
82+
// LLVM-NEXT: %[[#init:]] = tail call i32 @_Z11produce_intv()
83+
// LLVM-NEXT: store i32 %[[#init]], ptr %[[#slot]], align 4, !invariant.group !{{.+}}
84+
// LLVM-NEXT: call void @_Z8blackboxRKi(ptr nonnull %[[#slot]])
85+
// LLVM-NEXT: call void @_Z8blackboxRKi(ptr nonnull %[[#slot]])
86+
// LLVM-NEXT: ret i32 %[[#init]]
87+
// LLVM-NEXT: }

0 commit comments

Comments
 (0)