-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[CIR] Upstream X86 builtin clflush, fence and pause #167401
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
539eebe
de47432
26d8914
93c0ded
9eb5199
776f5c1
8a07992
e481360
a665f70
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,48 @@ | |
| using namespace clang; | ||
| using namespace clang::CIRGen; | ||
|
|
||
| /// Get integer from a mlir::Value that is an int constant or a constant op. | ||
| static int64_t getIntValueFromConstOp(mlir::Value val) { | ||
| return val.getDefiningOp<cir::ConstantOp>().getIntValue().getSExtValue(); | ||
| } | ||
|
|
||
| static mlir::Value emitClFlush(CIRGenFunction& cgf, | ||
| const CallExpr* e, | ||
| mlir::Value& op) { | ||
| mlir::Type voidTy = cir::VoidType::get(&cgf.getMLIRContext()); | ||
| mlir::Location location = cgf.getLoc(e->getExprLoc()); | ||
| return cir::LLVMIntrinsicCallOp::create( | ||
| cgf.getBuilder(), location, | ||
| cgf.getBuilder().getStringAttr("x86.sse2.clflush"), voidTy, op) | ||
| .getResult(); | ||
| } | ||
|
|
||
| static mlir::Value emitPrefetch(CIRGenFunction& cgf, | ||
| const CallExpr* e, | ||
| mlir::Value& addr, | ||
| int64_t hint) { | ||
| CIRGenBuilderTy& builder = cgf.getBuilder(); | ||
| mlir::Type voidTy = cir::VoidType::get(&cgf.getMLIRContext()); | ||
HendrikHuebner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| mlir::Type sInt32Ty = cir::IntType::get(&cgf.getMLIRContext(), 32, true); | ||
HendrikHuebner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| mlir::Value address = builder.createPtrBitcast(addr, voidTy); | ||
| mlir::Location location = cgf.getLoc(e->getExprLoc()); | ||
| mlir::Value rw = | ||
| cir::ConstantOp::create(builder, location, | ||
| cir::IntAttr::get(sInt32Ty, (hint >> 2) & 0x1)); | ||
HendrikHuebner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| mlir::Value locality = | ||
| cir::ConstantOp::create(builder, location, | ||
| cir::IntAttr::get(sInt32Ty, hint & 0x3)); | ||
HendrikHuebner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| mlir::Value data = cir::ConstantOp::create(builder, location, | ||
| cir::IntAttr::get(sInt32Ty, 1)); | ||
HendrikHuebner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| return cir::LLVMIntrinsicCallOp::create( | ||
| builder, location, | ||
| builder.getStringAttr("prefetch"), voidTy, | ||
| mlir::ValueRange{address, rw, locality, data}) | ||
| .getResult(); | ||
| } | ||
|
|
||
|
|
||
| mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID, | ||
| const CallExpr *e) { | ||
| if (builtinID == Builtin::BI__builtin_cpu_is) { | ||
|
|
@@ -43,11 +85,28 @@ mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID, | |
| // Find out if any arguments are required to be integer constant expressions. | ||
| assert(!cir::MissingFeatures::handleBuiltinICEArguments()); | ||
|
|
||
| // The operands of the builtin call | ||
| llvm::SmallVector<mlir::Value, 4> ops; | ||
|
|
||
| // `ICEArguments` is a bitmap indicating whether the argument at the i-th bit | ||
| // is required to be a constant integer expression. | ||
| unsigned ICEArguments = 0; | ||
HendrikHuebner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ASTContext::GetBuiltinTypeError error; | ||
| getContext().GetBuiltinType(builtinID, error, &ICEArguments); | ||
| assert(error == ASTContext::GE_None && "Error while getting builtin type."); | ||
|
|
||
| const unsigned numArgs = e->getNumArgs(); | ||
| for (unsigned i = 0; i != numArgs; i++) { | ||
| ops.push_back(emitScalarOrConstFoldImmArg(ICEArguments, i, e)); | ||
| } | ||
|
|
||
| switch (builtinID) { | ||
| default: | ||
| return {}; | ||
| case X86::BI_mm_prefetch: | ||
| return emitPrefetch(*this, e, ops[0], getIntValueFromConstOp(ops[1])); | ||
|
||
| case X86::BI_mm_clflush: | ||
| return emitClFlush(*this, e, ops[0]); | ||
| case X86::BI_mm_lfence: | ||
| case X86::BI_mm_pause: | ||
| case X86::BI_mm_mfence: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1430,6 +1430,28 @@ mlir::Value CIRGenFunction::emitPromotedScalarExpr(const Expr *e, | |
| return ScalarExprEmitter(*this, builder).Visit(const_cast<Expr *>(e)); | ||
| } | ||
|
|
||
| mlir::Value CIRGenFunction::emitScalarOrConstFoldImmArg(unsigned ICEArguments, | ||
|
||
| unsigned index, | ||
| const CallExpr *e) { | ||
| mlir::Value arg{}; | ||
|
|
||
| // The bit at the specified index indicates whether the argument is required | ||
| // to be a constant integer expression. | ||
| bool isArgRequiredToBeConstant = (ICEArguments & (1 << index)); | ||
|
|
||
| if (!isArgRequiredToBeConstant) { | ||
| arg = emitScalarExpr(e->getArg(index)); | ||
| } else { | ||
| // If this is required to be a constant, constant fold it so that we | ||
| // know that the generated intrinsic gets a ConstantInt. | ||
| std::optional<llvm::APSInt> result = | ||
| e->getArg(index)->getIntegerConstantExpr(getContext()); | ||
| assert(result && "Expected argument to be a constant"); | ||
| arg = builder.getConstInt(getLoc(e->getSourceRange()), *result); | ||
| } | ||
| return arg; | ||
| } | ||
|
|
||
| [[maybe_unused]] static bool mustVisitNullValue(const Expr *e) { | ||
| // If a null pointer expression's type is the C++0x nullptr_t and | ||
| // the expression is not a simple literal, it must be evaluated | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| // 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 | ||
| // RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s | ||
| // 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 | ||
| // RUN: FileCheck --check-prefixes=LLVM --input-file=%t.ll %s | ||
|
|
||
| // 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 | ||
| // RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s | ||
| // 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 | ||
| // RUN: FileCheck --check-prefixes=LLVM --input-file=%t.ll %s | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you also add the OGCG checks. It's useful to manually verify that we're generating equivalent code. |
||
| // This test mimics clang/test/CodeGen/X86/sse-builtins.c, which eventually | ||
| // CIR shall be able to support fully. | ||
|
|
||
| #include <immintrin.h> | ||
|
|
||
|
|
||
| void test_mm_prefetch(char const* p) { | ||
| // CIR-LABEL: test_mm_prefetch | ||
| // LLVM-LABEL: test_mm_prefetch | ||
| _mm_prefetch(p, 0); | ||
| // CIR: cir.prefetch read locality(0) %{{.*}} : !cir.ptr<!void> | ||
| // LLVM: call void @llvm.prefetch.p0(ptr {{.*}}, i32 0, i32 0, i32 1) | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,23 @@ | ||||||
| // 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 | ||||||
| // RUN: FileCheck --check-prefixes=CIR-CHECK --input-file=%t.cir %s | ||||||
|
||||||
| // RUN: FileCheck --check-prefixes=CIR-CHECK --input-file=%t.cir %s | |
| // RUN: FileCheck --check-prefixes=CIR --input-file=%t.cir %s |
Can you replace all instances of CIR-CHECK with CIR and LLVM-CHECK with LLVM? I'm not sure what the person who implemented that in the incubator was trying to accomplish, but it's not necessary. As currently written, this test will ignore the CIR-LABEL and LLVM-LABEL checks. Those are label checks for the CIR and LLVM prefixes.
Also, please add OGCG checks here too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. What is the point of adding CIR-LABEL instead of just CIR? I've seen CIR-NEXT and CIR-SAME as well, are there docs about what these do?
Edit: Ahh I found this: https://llvm.org/docs/CommandGuide/FileCheck.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andykaylor Hi, you mentioned you would like to see this PR merged before the other builtin upstreaming PRs, do you think we can land it now?
Uh oh!
There was an error while loading. Please reload this page.