[clang] [CIR] Upstream Inline Asm with Input Operands (PR #176239)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 15 12:28:48 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Erich Keane (erichkeane)
<details>
<summary>Changes</summary>
Like the previous patch, this upstreams inline
assembly support re #<!-- -->153267
This patch extends this to Input Operands. With this done, we now correctly compile every test in the incubator for inline assembly.
NOTE: When doing the IR differences between CIR and Classic Codegen, I noticed a sext/zext difference, but I'm reasonably convinced CIR is correct. Also, we keep around the 'ret-val' temporary for a function,
so that difference is also in the tests.
---
Patch is 50.08 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/176239.diff
4 Files Affected:
- (modified) clang/include/clang/CIR/MissingFeatures.h (-1)
- (modified) clang/lib/CIR/CodeGen/CIRGenAsm.cpp (+192-15)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+7)
- (modified) clang/test/CIR/CodeGen/inline-asm.c (+751-2)
``````````diff
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 39818417fc3d0..2f3814eac0ce7 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -214,7 +214,6 @@ struct MissingFeatures {
static bool appleKext() { return false; }
static bool armComputeVolatileBitfields() { return false; }
static bool asmGoto() { return false; }
- static bool asmInputOperands() { return false; }
static bool asmLabelAttr() { return false; }
static bool asmLLVMAssume() { return false; }
static bool asmMemoryEffects() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenAsm.cpp b/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
index 7032781dc97f8..ae60d1a6c4a2a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
@@ -117,6 +117,67 @@ collectInOutConstraintInfos(const CIRGenFunction &cgf, const AsmStmt &s,
}
}
+std::pair<mlir::Value, mlir::Type> CIRGenFunction::emitAsmInputLValue(
+ const TargetInfo::ConstraintInfo &info, LValue inputValue,
+ QualType inputType, std::string &constraintString, SourceLocation loc) {
+
+ if (info.allowsRegister() || !info.allowsMemory()) {
+ if (hasScalarEvaluationKind(inputType))
+ return {emitLoadOfLValue(inputValue, loc).getValue(), mlir::Type()};
+
+ mlir::Type ty = convertType(inputType);
+ uint64_t size = cgm.getDataLayout().getTypeSizeInBits(ty);
+ if ((size <= 64 && llvm::isPowerOf2_64(size)) ||
+ getTargetHooks().isScalarizableAsmOperand(*this, ty)) {
+ ty = cir::IntType::get(&getMLIRContext(), size, false);
+
+ return {builder.createLoad(
+ getLoc(loc),
+ inputValue.getAddress().withElementType(builder, ty)),
+ mlir::Type()};
+ }
+ }
+
+ Address addr = inputValue.getAddress();
+ constraintString += '*';
+ return {addr.getPointer(), addr.getElementType()};
+}
+
+std::pair<mlir::Value, mlir::Type>
+CIRGenFunction::emitAsmInput(const TargetInfo::ConstraintInfo &info,
+ const Expr *inputExpr,
+ std::string &constraintString) {
+ auto loc = getLoc(inputExpr->getExprLoc());
+
+ // If this can't be a register or memory, i.e., has to be a constant
+ // (immediate or symbolic), try to emit it as such.
+ if (!info.allowsRegister() && !info.allowsMemory()) {
+ if (info.requiresImmediateConstant()) {
+ Expr::EvalResult evalResult;
+ inputExpr->EvaluateAsRValue(evalResult, getContext(), true);
+
+ llvm::APSInt intResult;
+ if (evalResult.Val.toIntegralConstant(intResult, inputExpr->getType(),
+ getContext()))
+ return {builder.getConstInt(loc, intResult), mlir::Type()};
+ }
+
+ Expr::EvalResult result;
+ if (inputExpr->EvaluateAsInt(result, getContext()))
+ return {builder.getConstInt(loc, result.Val.getInt()), mlir::Type()};
+ }
+
+ if (info.allowsRegister() || !info.allowsMemory())
+ if (CIRGenFunction::hasScalarEvaluationKind(inputExpr->getType()))
+ return {emitScalarExpr(inputExpr), mlir::Type()};
+ if (inputExpr->getStmtClass() == Expr::CXXThisExprClass)
+ return {emitScalarExpr(inputExpr), mlir::Type()};
+ inputExpr = inputExpr->IgnoreParenNoopCasts(getContext());
+ LValue dest = emitLValue(inputExpr);
+ return emitAsmInputLValue(info, dest, inputExpr->getType(), constraintString,
+ inputExpr->getExprLoc());
+}
+
static void emitAsmStores(CIRGenFunction &cgf, const AsmStmt &s,
const llvm::ArrayRef<mlir::Value> regResults,
const llvm::ArrayRef<mlir::Type> resultRegTypes,
@@ -227,6 +288,11 @@ mlir::LogicalResult CIRGenFunction::emitAsmStmt(const AsmStmt &s) {
llvm::BitVector resultTypeRequiresCast;
llvm::BitVector resultRegIsFlagReg;
+ // Keep track of input constraints.
+ std::string inOutConstraints;
+ std::vector<mlir::Type> inOutArgTypes;
+ std::vector<mlir::Type> inOutArgElemTypes;
+
// Keep track of out constraints for tied input operand.
SmallVector<std::string> outputConstraints;
@@ -241,11 +307,6 @@ mlir::LogicalResult CIRGenFunction::emitAsmStmt(const AsmStmt &s) {
// in addition to meeting the conditions listed above.
bool readOnly = true, readNone = true;
- if (s.getNumInputs() != 0) {
- assert(!cir::MissingFeatures::asmInputOperands());
- cgm.errorNYI(srcLoc, "asm with input operands");
- }
-
std::string outputConstraint;
for (unsigned i = 0, e = s.getNumOutputs(); i != e; ++i) {
TargetInfo::ConstraintInfo &info = outputConstraintInfos[i];
@@ -310,15 +371,31 @@ mlir::LogicalResult CIRGenFunction::emitAsmStmt(const AsmStmt &s) {
// If this output is tied to an input, and if the input is larger, then
// we need to set the actual result type of the inline asm node to be the
// same as the input type.
- if (info.hasMatchingInput())
- assert(!cir::MissingFeatures::asmInputOperands());
+ if (info.hasMatchingInput()) {
+ unsigned inputNo;
+ for (inputNo = 0; inputNo != s.getNumInputs(); ++inputNo) {
+ TargetInfo::ConstraintInfo &input = inputConstraintInfos[inputNo];
+ if (input.hasTiedOperand() && input.getTiedOperand() == i)
+ break;
+ }
+ assert(inputNo != s.getNumInputs() && "Didn't find matching input!");
- if (mlir::Type adjTy = cgm.getTargetCIRGenInfo().adjustInlineAsmType(
- *this, outputConstraint, resultRegTypes.back()))
- resultRegTypes.back() = adjTy;
- else
- cgm.getDiags().Report(srcLoc, diag::err_asm_invalid_type_in_input)
- << outExpr->getType() << outputConstraint;
+ QualType inputTy = s.getInputExpr(inputNo)->getType();
+ QualType outputType = outExpr->getType();
+
+ uint64_t inputSize = getContext().getTypeSize(inputTy);
+ if (getContext().getTypeSize(outputType) < inputSize) {
+ // Form the asm to return the value as a larger integer or fp type.
+ resultRegTypes.back() = convertType(inputTy);
+ }
+
+ if (mlir::Type adjTy = cgm.getTargetCIRGenInfo().adjustInlineAsmType(
+ *this, outputConstraint, resultRegTypes.back()))
+ resultRegTypes.back() = adjTy;
+ else
+ cgm.getDiags().Report(srcLoc, diag::err_asm_invalid_type_in_input)
+ << outExpr->getType() << outputConstraint;
+ }
// Update largest vector width for any vector types.
assert(!cir::MissingFeatures::asmVectorType());
@@ -343,11 +420,111 @@ mlir::LogicalResult CIRGenFunction::emitAsmStmt(const AsmStmt &s) {
readOnly = readNone = false;
}
- if (info.isReadWrite())
- assert(!cir::MissingFeatures::asmInputOperands());
+ if (info.isReadWrite()) {
+ inOutConstraints += ',';
+ const Expr *inputExpr = s.getOutputExpr(i);
+
+ auto [arg, argElementType] =
+ emitAsmInputLValue(info, dest, inputExpr->getType(), inOutConstraints,
+ inputExpr->getExprLoc());
+
+ if (mlir::Type adjTy = getTargetHooks().adjustInlineAsmType(
+ *this, outputConstraint, arg.getType()))
+ arg = builder.createBitcast(arg, adjTy);
+
+ // Update largest vector width for any vector types.
+ assert(!cir::MissingFeatures::asmVectorType());
+
+ // Only tie earlyclobber physregs.
+ if (info.allowsRegister() && (gccReg.empty() || info.earlyClobber()))
+ inOutConstraints += llvm::utostr(i);
+ else
+ inOutConstraints += outputConstraint;
+
+ inOutArgTypes.push_back(arg.getType());
+ inOutArgElemTypes.push_back(argElementType);
+ inOutArgs.push_back(arg);
+ }
} // iterate over output operands
+ for (unsigned i = 0, e = s.getNumInputs(); i != e; ++i) {
+ TargetInfo::ConstraintInfo &info = inputConstraintInfos[i];
+ const Expr *inputExpr = s.getInputExpr(i);
+
+ if (info.allowsMemory())
+ readNone = false;
+
+ if (!constraints.empty())
+ constraints += ',';
+
+ std::string inputConstraint(s.getInputConstraint(i));
+ inputConstraint =
+ getTarget().simplifyConstraint(inputConstraint, &outputConstraintInfos);
+
+ inputConstraint = s.addVariableConstraints(
+ inputConstraint, *inputExpr->IgnoreParenNoopCasts(getContext()),
+ getTarget(), /*EarlyClobber=*/false,
+ [&](const Stmt *unspStmt, StringRef msg) {
+ cgm.errorUnsupported(unspStmt, msg);
+ });
+
+ std::string replaceConstraint(inputConstraint);
+ auto [arg, argElemType] = emitAsmInput(info, inputExpr, constraints);
+
+ // If this input argument is tied to a larger output result, extend the
+ // input to be the same size as the output. The LLVM backend wants to see
+ // the input and output of a matching constraint be the same size. Note
+ // that GCC does not define what the top bits are here. We use zext because
+ // that is usually cheaper, but LLVM IR should really get an anyext someday.
+ if (info.hasTiedOperand()) {
+ unsigned output = info.getTiedOperand();
+ QualType outputType = s.getOutputExpr(output)->getType();
+ QualType inputTy = inputExpr->getType();
+
+ if (getContext().getTypeSize(outputType) >
+ getContext().getTypeSize(inputTy)) {
+ // Use ptrtoint as appropriate so that we can do our extension.
+ if (isa<cir::PointerType>(arg.getType()))
+ arg = builder.createPtrToInt(arg, uIntPtrTy);
+ mlir::Type outputTy = convertType(outputType);
+ if (isa<cir::IntType>(outputTy))
+ arg = builder.createIntCast(arg, outputTy);
+ else if (isa<cir::PointerType>(outputTy))
+ arg = builder.createIntCast(arg, uIntPtrTy);
+ else if (isa<cir::FPTypeInterface>(outputTy))
+ arg = builder.createFloatingCast(arg, outputTy);
+ }
+
+ // Deal with the tied operands' constraint code in adjustInlineAsmType.
+ replaceConstraint = outputConstraints[output];
+ }
+
+ if (mlir::Type adjTy = getTargetHooks().adjustInlineAsmType(
+ *this, replaceConstraint, arg.getType()))
+ arg = builder.createBitcast(arg, adjTy);
+ else
+ cgm.getDiags().Report(s.getAsmLoc(), diag::err_asm_invalid_type_in_input)
+ << inputExpr->getType() << inputConstraint;
+
+ // Update largest vector width for any vector types.
+ assert(!cir::MissingFeatures::asmVectorType());
+
+ argTypes.push_back(arg.getType());
+ argElemTypes.push_back(argElemType);
+ inArgs.push_back(arg);
+ args.push_back(arg);
+ constraints += inputConstraint;
+ } // iterate over input operands
+
+ // Append the "input" part of inout constraints.
+ for (unsigned i = 0, e = inOutArgs.size(); i != e; ++i) {
+ args.push_back(inOutArgs[i]);
+ argTypes.push_back(inOutArgTypes[i]);
+ argElemTypes.push_back(inOutArgElemTypes[i]);
+ }
+ constraints += inOutConstraints;
+
bool hasUnwindClobber = false;
collectClobbers(*this, s, constraints, hasUnwindClobber, readOnly, readNone);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 98b3f8384fb75..27af920ea7703 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1360,6 +1360,13 @@ class CIRGenFunction : public CIRGenTypeCache {
Address emitArrayToPointerDecay(const Expr *e,
LValueBaseInfo *baseInfo = nullptr);
+ std::pair<mlir::Value, mlir::Type>
+ emitAsmInputLValue(const TargetInfo::ConstraintInfo &info, LValue inputValue,
+ QualType inputType, std::string &constraintString,
+ SourceLocation loc);
+ std::pair<mlir::Value, mlir::Type>
+ emitAsmInput(const TargetInfo::ConstraintInfo &info, const Expr *inputExpr,
+ std::string &constraintString);
mlir::LogicalResult emitAsmStmt(const clang::AsmStmt &s);
RValue emitAtomicExpr(AtomicExpr *e);
diff --git a/clang/test/CIR/CodeGen/inline-asm.c b/clang/test/CIR/CodeGen/inline-asm.c
index adcd0c96905a0..55570e1e05550 100644
--- a/clang/test/CIR/CodeGen/inline-asm.c
+++ b/clang/test/CIR/CodeGen/inline-asm.c
@@ -1,9 +1,9 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
-// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefixes=LLVM
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefixes=LLVM,CIRLLVMONLY
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t-cir.ll
-// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefixes=LLVM
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefixes=LLVM,LLVMONLY
__asm__ ("foo1");
__asm__ ("foo2");
@@ -37,6 +37,34 @@ void empty2() {
__asm__ volatile("nop" : : : );
}
+// CIR: cir.func{{.*}}@empty3
+// CIR: %[[X:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init]
+// CIR: cir.asm(x86_att,
+// CIR-NEXT: out = [%[[X]] : !cir.ptr<!s32i> (maybe_memory)],
+// CIR-NEXT: in = [],
+// CIR-NEXT: in_out = [%[[X]] : !cir.ptr<!s32i> (maybe_memory)],
+// CIR-NEXT: {"" "=*m,*m,~{dirflag},~{fpsr},~{flags}"}) side_effects
+// LLVM: define{{.*}}@empty3
+// LLVM: %[[X:.*]] = alloca i32
+// LLVM: call void asm sideeffect "", "=*m,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %[[X]], ptr elementtype(i32) %[[X]])
+void empty3(int x) {
+ __asm__ volatile("" : "+m"(x));
+}
+
+// CIR: cir.func{{.*}}@empty4
+// CIR: %[[X:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init]
+// CIR: cir.asm(x86_att,
+// CIR-NEXT: out = [],
+// CIR-NEXT: in = [%[[X]] : !cir.ptr<!s32i> (maybe_memory)],
+// CIR-NEXT: in_out = [],
+// CIR-NEXT: {"" "*m,~{dirflag},~{fpsr},~{flags}"}) side_effects
+// LLVM: define{{.*}}@empty4
+// LLVM: %[[X:.*]] = alloca i32
+// LLVM: call void asm sideeffect "", "*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %[[X]])
+void empty4(int x) {
+ __asm__ volatile("" : : "m"(x));
+}
+
// CIR: cir.func{{.*}}@empty5
// CIR: %[[X:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init]
// CIR: cir.asm(x86_att,
@@ -51,6 +79,95 @@ void empty5(int x) {
__asm__ volatile("" : "=m"(x));
}
+// CIR: cir.func{{.*}}@empty6
+// CIR: %[[X:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init]
+// CIR: %[[X_LOAD:.*]] = cir.load align(4) %[[X]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.asm(x86_att,
+// CIR-NEXT: out = [],
+// CIR-NEXT: in = [],
+// CIR-NEXT: in_out = [%[[X_LOAD]] : !s32i],
+// CIR-NEXT: {"" "=&r,=&r,1,~{dirflag},~{fpsr},~{flags}"}) side_effects -> !rec_anon_struct
+// LLVM: define{{.*}}@empty6
+// LLVM: %[[X:.*]] = alloca i32
+// LLVM: %[[X_LOAD:.*]] = load i32, ptr %[[X]]
+// LLVM: call { i32, i32 } asm sideeffect "", "=&r,=&r,1,~{dirflag},~{fpsr},~{flags}"(i32 %[[X_LOAD]])
+//
+void empty6(int x) {
+ __asm__ volatile("" : "=&r"(x), "+&r"(x));
+}
+
+// CIR: cir.func{{.*}}@add1
+// CIR: %[[X:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["x", init]
+// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a"]
+// CIR: %[[X_LOAD:.*]] = cir.load align(4) %[[X]] : !cir.ptr<!u32i>, !u32i
+// CIR: %[[ASM_RES:.*]] = cir.asm(x86_att,
+// CIR-NEXT: out = [],
+// CIR-NEXT: in = [%[[X_LOAD]] : !u32i],
+// CIR-NEXT: in_out = [],
+// CIR-NEXT: {"addl $$42, $1" "=r,r,~{dirflag},~{fpsr},~{flags}"}) -> !s32i
+// CIR-NEXT: cir.store{{.*}} %[[ASM_RES]], %[[A]] : !s32i, !cir.ptr<!s32i>
+// LLVM: define{{.*}}@add1
+// LLVM: %[[X:.*]] = alloca i32
+// Classic codegen doesn't alloca for a return slot.
+// CIRLLVMONLY: alloca i32
+// LLVM: %[[A:.*]] = alloca i32
+// LLVM: %[[X_LOAD:.*]] = load i32, ptr %[[X]]
+// LLVM: %[[ASM_RES:.*]] = call i32 asm "addl $$42, $1", "=r,r,~{dirflag},~{fpsr},~{flags}"(i32 %[[X_LOAD]])
+// LLVM: store i32 %[[ASM_RES]], ptr %[[A]]
+unsigned add1(unsigned int x) {
+ int a;
+ __asm__("addl $42, %[val]"
+ : "=r" (a)
+ : [val] "r" (x)
+ );
+
+ return a;
+}
+
+// CIR: cir.func{{.*}}@add2
+// CIR: %[[X:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["x", init]
+// CIR: %[[X_LOAD:.*]] = cir.load align(4) %[[X]] : !cir.ptr<!u32i>, !u32i
+// CIR: %[[ASM_RES:.*]] = cir.asm(x86_att,
+// CIR-NEXT: out = [],
+// CIR-NEXT: in = [],
+// CIR-NEXT: in_out = [%[[X_LOAD]] : !u32i],
+// CIR-NEXT: {"addl $$42, $0" "=r,0,~{dirflag},~{fpsr},~{flags}"}) -> !u32i
+// CIR-NEXT: cir.store{{.*}} %[[ASM_RES]], %[[X]] : !u32i, !cir.ptr<!u32i>
+// LLVM: define{{.*}}@add2
+// LLVM: %[[X:.*]] = alloca i32
+// LLVM: %[[X_LOAD:.*]] = load i32, ptr %[[X]]
+// LLVM: %[[ASM_RES:.*]] = call i32 asm "addl $$42, $0", "=r,0,~{dirflag},~{fpsr},~{flags}"(i32 %[[X_LOAD]])
+// LLVM: store i32 %[[ASM_RES]], ptr %[[X]]
+unsigned add2(unsigned int x) {
+ __asm__("addl $42, %[val]"
+ : [val] "+r" (x)
+ );
+ return x;
+}
+
+// CIR: cir.func{{.*}}@add3
+// CIR: %[[X:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["x", init]
+// CIR: %[[X_LOAD:.*]] = cir.load{{.*}} %[[X]] : !cir.ptr<!u32i>, !u32i
+// CIR: %[[ASM_RES:.*]] = cir.asm(x86_att,
+// CIR-NEXT: out = [],
+// CIR-NEXT: in = [],
+// CIR-NEXT: in_out = [%[[X_LOAD]] : !u32i],
+// CIR-NEXT: {"addl $$42, $0 \0A\09 subl $$1, $0 \0A\09 imul $$2, $0" "=r,0,~{dirflag},~{fpsr},~{flags}"}) -> !u32i
+// CIR-NEXT: cir.store{{.*}} %[[ASM_RES]], %[[X]] : !u32i, !cir.ptr<!u32i>
+// LLVM: define{{.*}}@add3
+// LLVM: %[[X:.*]] = alloca i32
+// LLVM: %[[X_LOAD:.*]] = load i32, ptr %[[X]]
+// LLVM: %[[ASM_RES:.*]] = call i32 asm "addl $$42, $0 \0A\09 subl $$1, $0 \0A\09 imul $$2, $0", "=r,0,~{dirflag},~{fpsr},~{flags}"(i32 %[[X_LOAD]])
+// LLVM: store i32 %[[ASM_RES]], ptr %[[X]]
+unsigned add3(unsigned int x) { // ((42 + x) - 1) * 2
+ __asm__("addl $42, %[val] \n\t\
+ subl $1, %[val] \n\t\
+ imul $2, %[val]"
+ : [val] "+r" (x)
+ );
+ return x;
+}
+
// CIR: cir.func{{.*}}@add4
// CIR: %[[X:.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["x", init]
// CIR: %[[X_LOAD:.*]] = cir.load {{.*}} %[[X]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
@@ -68,6 +185,36 @@ void add4(int *x) {
__asm__("addl $42, %[addr]" : [addr] "=m" (*x));
}
+// CIR: cir.func{{.*}}@add5
+// CIR: %[[X:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["x", init]
+// CIR: %[[Y:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["y", init]
+// CIR: %[[R:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["r"]
+// CIR: %[[X_LOAD:.*]] = cir.load{{.*}} %[[X]] : !cir.ptr<!cir.float>, !cir.float
+// CIR: %[[Y_LOAD:.*]] = cir.load{{.*}} %[[Y]] : !cir.ptr<!cir.float>, !cir.float
+// CIR: %[[ASM_RES:.*]] = cir.asm(x86_att,
+// CIR-NEXT: out = [],
+// CIR-NEXT: in = [%[[X_LOAD]] : !cir.float, %[[Y_LOAD]] : !cir.float],
+// CIR-NEXT: in_out = [],
+// CIR-NEXT: {"flds $1; flds $2; faddp" "=&{st},imr,imr,~{dirflag},~{fpsr},~{flags}"}) -> !cir.float
+// CIR-NEXT: cir.store{{.*}} %[[ASM_RES]], %[[R]] : !cir.float, !cir.ptr<!cir.float>
+// LLVM: define{{.*}}@add5
+// LLVM: %[[X:.*]] = alloca float
+// LLVM: %[[Y:.*]] = alloca float
+// Classic codegen doesn't alloca for a return slot.
+// CIRLLVMONLY: alloca float
+// LLVM: %[[R:.*]] = alloca float
+// LLVM: %[[X_LOAD:.*]] = load float, ptr %[[X]]
+// LLVM: %[[Y_LOAD:.*]] = load float, ptr %[[Y]]
+// LLVM: %[[ASM_RES:.*]] = call float asm "flds $1; flds $2; faddp", "=&{st},imr,imr,~{dirflag},~{fpsr},~{flags}"(float %[[X_LOAD]], float %[[Y_LOAD]])
+// LLVM: store float %[[ASM_RES]], ptr %[[R]]
+float add5(float x, float y) {
+ float r;
+ __asm__("flds %[x]; flds %[y]; faddp"
+ : "=&t" (r)
+ : [x] "g" (x), [y] "g" (y));
+ return r;
+}
+
// CIR: cir.func{{.*}}@mov
// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a"] {alignment = 4 : i64}
// CIR: %[[RES:.*]] = cir.asm(x...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/176239
More information about the cfe-commits
mailing list