Skip to content

Commit 539eebe

Browse files
[CIR] Upstream X86 builtin _mm_prefetch and _mm_clflush
1 parent 0246f33 commit 539eebe

File tree

7 files changed

+191
-0
lines changed

7 files changed

+191
-0
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,18 @@ def CIR_ConstantOp : CIR_Op<"const", [
413413

414414
template <typename T>
415415
T getValueAttr() { return mlir::dyn_cast<T>(getValue()); }
416+
417+
llvm::APInt getIntValue() {
418+
if (const auto intAttr = getValueAttr<cir::IntAttr>())
419+
return intAttr.getValue();
420+
llvm_unreachable("Expected an IntAttr in ConstantOp");
421+
}
422+
423+
bool getBoolValue() {
424+
if (const auto boolAttr = getValueAttr<cir::BoolAttr>())
425+
return boolAttr.getValue();
426+
llvm_unreachable("Expected a BoolAttr in ConstantOp");
427+
}
416428
}];
417429

418430
let hasFolder = 1;
@@ -2579,6 +2591,39 @@ def CIR_FuncOp : CIR_Op<"func", [
25792591
}];
25802592
}
25812593

2594+
//===----------------------------------------------------------------------===//
2595+
// LLVMIntrinsicCallOp
2596+
//===----------------------------------------------------------------------===//
2597+
2598+
def CIR_LLVMIntrinsicCallOp : CIR_Op<"llvm.intrinsic"> {
2599+
let summary = "Call to llvm intrinsic functions that is not defined in CIR";
2600+
let description = [{
2601+
`cir.llvm.intrinsic` operation represents a call-like expression which has
2602+
return type and arguments that maps directly to a llvm intrinsic.
2603+
It only records intrinsic `intrinsic_name`.
2604+
}];
2605+
2606+
let results = (outs Optional<CIR_AnyType>:$result);
2607+
let arguments = (ins
2608+
StrAttr:$intrinsic_name, Variadic<CIR_AnyType>:$arg_ops);
2609+
2610+
let skipDefaultBuilders = 1;
2611+
2612+
let assemblyFormat = [{
2613+
$intrinsic_name $arg_ops `:` functional-type($arg_ops, $result) attr-dict
2614+
}];
2615+
2616+
let builders = [
2617+
OpBuilder<(ins "mlir::StringAttr":$intrinsic_name, "mlir::Type":$resType,
2618+
CArg<"mlir::ValueRange", "{}">:$operands), [{
2619+
$_state.addAttribute("intrinsic_name", intrinsic_name);
2620+
$_state.addOperands(operands);
2621+
if (resType)
2622+
$_state.addTypes(resType);
2623+
}]>,
2624+
];
2625+
}
2626+
25822627
//===----------------------------------------------------------------------===//
25832628
// CallOp
25842629
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,49 @@
2121
using namespace clang;
2222
using namespace clang::CIRGen;
2323

24+
/// Get integer from a mlir::Value that is an int constant or a constant op.
25+
static int64_t getIntValueFromConstOp(mlir::Value val) {
26+
return val.getDefiningOp<cir::ConstantOp>().getIntValue().getSExtValue();
27+
}
28+
29+
static mlir::Value emitClFlush(CIRGenFunction& cgf,
30+
const CallExpr* e,
31+
mlir::Value& op) {
32+
mlir::Type voidTy = cir::VoidType::get(&cgf.getMLIRContext());
33+
mlir::Location location = cgf.getLoc(e->getExprLoc());
34+
return cgf.getBuilder()
35+
.create<cir::LLVMIntrinsicCallOp>(
36+
location, cgf.getBuilder().getStringAttr("x86.sse2.clflush"),
37+
voidTy, op)
38+
.getResult();
39+
}
40+
41+
static mlir::Value emitPrefetch(CIRGenFunction& cgf,
42+
const CallExpr* e,
43+
mlir::Value& addr,
44+
int64_t hint) {
45+
CIRGenBuilderTy& builder = cgf.getBuilder();
46+
mlir::Type voidTy = cir::VoidType::get(&cgf.getMLIRContext());
47+
mlir::Type sInt32Ty = cir::IntType::get(&cgf.getMLIRContext(), 32, true);
48+
mlir::Value address = builder.createPtrBitcast(addr, voidTy);
49+
mlir::Location location = cgf.getLoc(e->getExprLoc());
50+
mlir::Value rw =
51+
cir::ConstantOp::create(builder, location,
52+
cir::IntAttr::get(sInt32Ty, (hint >> 2) & 0x1));
53+
mlir::Value locality =
54+
cir::ConstantOp::create(builder, location,
55+
cir::IntAttr::get(sInt32Ty, hint & 0x3));
56+
mlir::Value data = cir::ConstantOp::create(builder, location,
57+
cir::IntAttr::get(sInt32Ty, 1));
58+
59+
return cir::LLVMIntrinsicCallOp::create(
60+
builder, location,
61+
builder.getStringAttr("prefetch"), voidTy,
62+
mlir::ValueRange{address, rw, locality, data})
63+
.getResult();
64+
}
65+
66+
2467
mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID,
2568
const CallExpr *e) {
2669
if (builtinID == Builtin::BI__builtin_cpu_is) {
@@ -43,11 +86,28 @@ mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID,
4386
// Find out if any arguments are required to be integer constant expressions.
4487
assert(!cir::MissingFeatures::handleBuiltinICEArguments());
4588

89+
// The operands of the builtin call
90+
llvm::SmallVector<mlir::Value, 4> ops;
91+
92+
// `ICEArguments` is a bitmap indicating whether the argument at the i-th bit
93+
// is required to be a constant integer expression.
94+
unsigned ICEArguments = 0;
95+
ASTContext::GetBuiltinTypeError error;
96+
getContext().GetBuiltinType(builtinID, error, &ICEArguments);
97+
assert(error == ASTContext::GE_None && "Error while getting builtin type.");
98+
99+
const unsigned numArgs = e->getNumArgs();
100+
for (unsigned i = 0; i != numArgs; i++) {
101+
ops.push_back(emitScalarOrConstFoldImmArg(ICEArguments, i, e));
102+
}
103+
46104
switch (builtinID) {
47105
default:
48106
return {};
49107
case X86::BI_mm_prefetch:
108+
return emitPrefetch(*this, e, ops[0], getIntValueFromConstOp(ops[1]));
50109
case X86::BI_mm_clflush:
110+
return emitClFlush(*this, e, ops[0]);
51111
case X86::BI_mm_lfence:
52112
case X86::BI_mm_pause:
53113
case X86::BI_mm_mfence:

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,28 @@ mlir::Value CIRGenFunction::emitPromotedScalarExpr(const Expr *e,
14301430
return ScalarExprEmitter(*this, builder).Visit(const_cast<Expr *>(e));
14311431
}
14321432

1433+
mlir::Value CIRGenFunction::emitScalarOrConstFoldImmArg(unsigned ICEArguments,
1434+
unsigned index,
1435+
const CallExpr *e) {
1436+
mlir::Value arg{};
1437+
1438+
// The bit at the specified index indicates whether the argument is required
1439+
// to be a constant integer expression.
1440+
bool isArgRequiredToBeConstant = (ICEArguments & (1 << index));
1441+
1442+
if (!isArgRequiredToBeConstant) {
1443+
arg = emitScalarExpr(e->getArg(index));
1444+
} else {
1445+
// If this is required to be a constant, constant fold it so that we
1446+
// know that the generated intrinsic gets a ConstantInt.
1447+
std::optional<llvm::APSInt> result =
1448+
e->getArg(index)->getIntegerConstantExpr(getContext());
1449+
assert(result && "Expected argument to be a constant");
1450+
arg = builder.getConstInt(getLoc(e->getSourceRange()), *result);
1451+
}
1452+
return arg;
1453+
}
1454+
14331455
[[maybe_unused]] static bool mustVisitNullValue(const Expr *e) {
14341456
// If a null pointer expression's type is the C++0x nullptr_t and
14351457
// the expression is not a simple literal, it must be evaluated

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,10 @@ class CIRGenFunction : public CIRGenTypeCache {
15291529
mlir::Value emitScalarExpr(const clang::Expr *e,
15301530
bool ignoreResultAssign = false);
15311531

1532+
mlir::Value emitScalarOrConstFoldImmArg(unsigned ICEArguments,
1533+
unsigned index,
1534+
const CallExpr *e);
1535+
15321536
mlir::Value emitScalarPrePostIncDec(const UnaryOperator *e, LValue lv,
15331537
cir::UnaryOpKind kind, bool isPre);
15341538

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,20 @@ static mlir::LLVM::CallIntrinsicOp replaceOpWithCallLLVMIntrinsicOp(
320320
return callIntrinOp;
321321
}
322322

323+
mlir::LogicalResult CIRToLLVMLLVMIntrinsicCallOpLowering::matchAndRewrite(
324+
cir::LLVMIntrinsicCallOp op,
325+
OpAdaptor adaptor,
326+
mlir::ConversionPatternRewriter &rewriter) const {
327+
mlir::Type llvmResTy =
328+
getTypeConverter()->convertType(op->getResultTypes()[0]);
329+
if (!llvmResTy)
330+
return op.emitError("expected LLVM result type");
331+
StringRef name = op.getIntrinsicName();
332+
replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm." + name, llvmResTy,
333+
adaptor.getOperands());
334+
return mlir::success();
335+
}
336+
323337
/// IntAttr visitor.
324338
mlir::Value CIRAttrToValue::visitCirAttr(cir::IntAttr intAttr) {
325339
mlir::Location loc = parentOp->getLoc();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %clang_cc1 -x c -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fclangir -emit-cir -o %t.cir -Wall -Werror
2+
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
3+
// RUN: %clang_cc1 -x c -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fclangir -emit-llvm -o %t.ll -Wall -Werror
4+
// RUN: FileCheck --check-prefixes=LLVM --input-file=%t.ll %s
5+
6+
// RUN: %clang_cc1 -x c++ -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fno-signed-char -fclangir -emit-cir -o %t.cir -Wall -Werror
7+
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
8+
// RUN: %clang_cc1 -x c++ -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fclangir -emit-llvm -o %t.ll -Wall -Werror
9+
// RUN: FileCheck --check-prefixes=LLVM --input-file=%t.ll %s
10+
11+
// This test mimics clang/test/CodeGen/X86/sse-builtins.c, which eventually
12+
// CIR shall be able to support fully.
13+
14+
#include <immintrin.h>
15+
16+
17+
void test_mm_prefetch(char const* p) {
18+
// CIR-LABEL: test_mm_prefetch
19+
// LLVM-LABEL: test_mm_prefetch
20+
_mm_prefetch(p, 0);
21+
// CIR: cir.prefetch read locality(0) %{{.*}} : !cir.ptr<!void>
22+
// LLVM: call void @llvm.prefetch.p0(ptr {{.*}}, i32 0, i32 0, i32 1)
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %clang_cc1 -x c -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse2 -fclangir -emit-cir -o %t.cir -Wall -Werror
2+
// RUN: FileCheck --check-prefixes=CIR-CHECK --input-file=%t.cir %s
3+
// RUN: %clang_cc1 -x c -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse2 -fno-signed-char -fclangir -emit-cir -o %t.cir -Wall -Werror
4+
// RUN: FileCheck --check-prefixes=CIR-CHECK --input-file=%t.cir %s
5+
6+
// RUN: %clang_cc1 -x c++ -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse2 -fclangir -emit-llvm -o %t.ll -Wall -Werror
7+
// RUN: FileCheck --check-prefixes=LLVM-CHECK --input-file=%t.ll %s
8+
// RUN: %clang_cc1 -x c++ -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse2 -fno-signed-char -fclangir -emit-llvm -o %t.ll -Wall -Werror
9+
// RUN: FileCheck --check-prefixes=LLVM-CHECK --input-file=%t.ll %s
10+
11+
// This test mimics clang/test/CodeGen/X86/sse2-builtins.c, which eventually
12+
// CIR shall be able to support fully.
13+
14+
#include <immintrin.h>
15+
16+
17+
void test_mm_clflush(void* A) {
18+
// CIR-LABEL: test_mm_clflush
19+
// LLVM-LABEL: teh
20+
_mm_clflush(A);
21+
// CIR-CHECK: {{%.*}} = cir.llvm.intrinsic "x86.sse2.clflush" {{%.*}} : (!cir.ptr<!void>) -> !void
22+
// LLVM-CHECK: call void @llvm.x86.sse2.clflush(ptr {{%.*}})
23+
}

0 commit comments

Comments
 (0)